Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gaia integration with fixes #42

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions integrations/gaia/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#GAIA Integration
GAIA_NODE_URL= # the URL of the Gaia node (Ex: https://llama8b.gaia.domains/v1)
GAIA_API_KEY= # the API key for the Gaia node (Ex: gaia-xxxxxxxxxx)
GAIA_MODEL_NAME= # the name of model running on your Gaia node (Ex: llama)
148 changes: 148 additions & 0 deletions integrations/gaia/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Gaia agent example

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.

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.

## Environment variables

Add the following keys to a `.env` file:

```bash
WALLET_KEY= # the private key of the wallet
ENCRYPTION_KEY= # a second random 32 bytes encryption key for local db encryption
GAIA_API_KEY= # Your API key from https://gaianet.ai
GAIA_NODE_URL= # Your custom Gaia node URL or a public node, ex: https://llama8b.gaia.domains/v1
GAIA_MODEL_NAME= # Model name running in your Gaia node or a public node, ex: llama
```

You can generate random keys with the following command:

```bash
yarn gen:keys
```

> [!WARNING]
> Running the `gen:keys` script will overwrite the existing `.env` file.

## Usage

```tsx
import { Client, type XmtpEnv } from "@xmtp/node-sdk";
import OpenAI from "openai";
import { createSigner, getEncryptionKeyFromHex } from "@/helpers";

const { WALLET_KEY, ENCRYPTION_KEY, GAIA_NODE_URL, GAIA_API_KEY, GAIA_MODEL_NAME } = process.env;

if (!WALLET_KEY) {
throw new Error("WALLET_KEY must be set");
}

if (!ENCRYPTION_KEY) {
throw new Error("ENCRYPTION_KEY must be set");
}

/* Check if the Gaia API key is set */
if (!GAIA_API_KEY) {
throw new Error("GAIA_API_KEY must be set");
}

/* Check if the Gaia node's base URL is set */
if (!GAIA_NODE_URL) {
throw new Error("GAIA_NODE_URL must be set");
}

/* Check if the the model name for the Gaia node is set */
if (!GAIA_MODEL_NAME) {
throw new Error("GAIA_MODEL_NAME must be set");
}

const signer = createSigner(WALLET_KEY);
const encryptionKey = getEncryptionKeyFromHex(ENCRYPTION_KEY);
const openai = new OpenAI({
baseURL: GAIA_NODE_URL,
apiKey: GAIA_API_KEY
});

/* Set the environment to dev or production */
const env: XmtpEnv = "dev";

async function main() {
console.log(`Creating client on the '${env}' network...`);
const client = await Client.create(signer, encryptionKey, {
env,
});

console.log("Syncing conversations...");
/* Sync the conversations from the network to update the local db */
await client.conversations.sync();

console.log(
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}`,
);

console.log("Waiting for messages...");
const stream = client.conversations.streamAllMessages();

for await (const message of await stream) {
/* Ignore messages from the same agent or non-text messages */
if (
message?.senderInboxId.toLowerCase() === client.inboxId.toLowerCase() ||
message?.contentType?.typeId !== "text"
) {
continue;
}

console.log(
`Received message: ${message.content as string} by ${message.senderInboxId}`,
);

const conversation = client.conversations.getConversationById(
message.conversationId,
);

if (!conversation) {
console.log("Unable to find conversation, skipping");
continue;
}

try {
const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: message.content as string }],
model: GAIA_MODEL_NAME,
});

/* Get the AI response */
const response =
completion.choices[0]?.message?.content ||
"I'm not sure how to respond to that.";

console.log(`Sending AI response: ${response}`);
await conversation.send(response);
} catch (error) {
console.error("Error getting AI response:", error);
await conversation.send(
"Sorry, I encountered an error processing your message.",
);
}

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

main().catch(console.error);
```

## Run the agent

```bash
# git clone repo
git clone https://github.com/ephemeraHQ/xmtp-agent-examples.git
# go to the folder
cd xmtp-agent-examples
# install packages
yarn

cd integrations/gaia
yarn dev
```
126 changes: 126 additions & 0 deletions integrations/gaia/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Client, type XmtpEnv } from "@xmtp/node-sdk";
import OpenAI from "openai";
import { createSigner, getEncryptionKeyFromHex } from "@/helpers";

/* Get the wallet key associated to the public key of
* the agent and the encryption key for the local db
* that stores your agent's messages */
const {
WALLET_KEY,
ENCRYPTION_KEY,
GAIA_NODE_URL,
GAIA_API_KEY,
GAIA_MODEL_NAME,
} = process.env;

/* Check if the environment variables are set */
if (!WALLET_KEY) {
throw new Error("WALLET_KEY must be set");
}

/* Check if the encryption key is set */
if (!ENCRYPTION_KEY) {
throw new Error("ENCRYPTION_KEY must be set");
}

/* Check if the OpenAI API key is set */
if (!GAIA_API_KEY) {
throw new Error("GAIA_API_KEY must be set");
}

/* Check if the Gaia node's base URL is set */
if (!GAIA_NODE_URL) {
throw new Error("GAIA_NODE_URL must be set");
}

/* Check if the the model name for the Gaia node is set */
if (!GAIA_MODEL_NAME) {
throw new Error("GAIA_MODEL_NAME must be set");
}

/* Create the signer using viem and parse the encryption key for the local db */
const signer = createSigner(WALLET_KEY);

Check failure on line 42 in integrations/gaia/index.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe assignment of an error typed value

Check failure on line 42 in integrations/gaia/index.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe call of a(n) `error` type typed value
const encryptionKey = getEncryptionKeyFromHex(ENCRYPTION_KEY);

Check failure on line 43 in integrations/gaia/index.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe assignment of an error typed value

Check failure on line 43 in integrations/gaia/index.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe call of a(n) `error` type typed value

/* Initialize the OpenAI client */
const openai = new OpenAI({
baseURL: GAIA_NODE_URL,
apiKey: GAIA_API_KEY,
});

/* Set the environment to dev or production */
const env: XmtpEnv = "dev";

/**
* Main function to run the agent
*/
async function main() {
console.log(`Creating client on the '${env}' network...`);
/* Initialize the xmtp client */
const client = await Client.create(signer, encryptionKey, {

Check failure on line 60 in integrations/gaia/index.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe argument of type error typed assigned to a parameter of type `Signer`

Check failure on line 60 in integrations/gaia/index.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe argument of type error typed assigned to a parameter of type `Uint8Array<ArrayBufferLike>`
env,
});

console.log("Syncing conversations...");
/* Sync the conversations from the network to update the local db */
await client.conversations.sync();

console.log(
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}`,
);

console.log("Waiting for messages...");
/* Stream all messages from the network */
const stream = client.conversations.streamAllMessages();

for await (const message of await stream) {
/* Ignore messages from the same agent or non-text messages */
if (
message?.senderInboxId.toLowerCase() === client.inboxId.toLowerCase() ||
message?.contentType?.typeId !== "text"
) {
continue;
}

console.log(
`Received message: ${message.content as string} by ${message.senderInboxId}`,
);

/* Get the conversation from the local db */
const conversation = client.conversations.getConversationById(
message.conversationId,
);

/* If the conversation is not found, skip the message */
if (!conversation) {
console.log("Unable to find conversation, skipping");
continue;
}

try {
/* Get the AI response */
const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: message.content as string }],
model: GAIA_MODEL_NAME as string,
});

/* Get the AI response */
const response =
completion.choices[0]?.message?.content ||
"I'm not sure how to respond to that.";

console.log(`Sending AI response: ${response}`);
/* Send the AI response to the conversation */
await conversation.send(response);
} catch (error) {
console.error("Error getting AI response:", error);
await conversation.send(
"Sorry, I encountered an error processing your message.",
);
}

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

main().catch(console.error);
23 changes: 23 additions & 0 deletions integrations/gaia/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@integrations/gaia",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"build": "cd ../.. && yarn tsc",
"dev": "tsx --env-file=.env index.ts",
"gen:keys": "tsx ../../scripts/generateKeys.ts",
"lint": "cd ../.. && yarn eslint integrations/gaia",
"typecheck": "cd ../.. && yarn typecheck"
},
"dependencies": {
"@xmtp/node-sdk": "0.0.42",
"tsx": "^4.19.2",
"uint8arrays": "^5.1.0",
"viem": "^2.22.17"
},
"packageManager": "yarn@4.6.0",
"engines": {
"node": ">=20"
}
}
9 changes: 9 additions & 0 deletions integrations/gaia/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": {
"@/helpers": ["./helpers/index.ts"]
}
},
"include": ["**/*.ts"]
}
11 changes: 11 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,17 @@ __metadata:
languageName: node
linkType: hard

"@integrations/gaia@workspace:integrations/gaia":
version: 0.0.0-use.local
resolution: "@integrations/gaia@workspace:integrations/gaia"
dependencies:
"@xmtp/node-sdk": "npm:0.0.42"
tsx: "npm:^4.19.2"
uint8arrays: "npm:^5.1.0"
viem: "npm:^2.22.17"
languageName: unknown
linkType: soft

"@integrations/gpt@workspace:integrations/grok":
version: 0.0.0-use.local
resolution: "@integrations/gpt@workspace:integrations/grok"
Expand Down
Loading