Skip to content

Commit

Permalink
Merge branch 'develop' into inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
monilpat authored Jan 11, 2025
2 parents 4ef4b6a + 1ca639e commit 8d831fa
Show file tree
Hide file tree
Showing 18 changed files with 3,168 additions and 4,779 deletions.
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"@elizaos/plugin-near": "workspace:*",
"@elizaos/plugin-zksync-era": "workspace:*",
"@elizaos/plugin-twitter": "workspace:*",
"@elizaos/plugin-primus": "workspace:*",
"@elizaos/plugin-cronoszkevm": "workspace:*",
"@elizaos/plugin-3d-generation": "workspace:*",
"@elizaos/plugin-fuel": "workspace:*",
Expand Down
17 changes: 17 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { SlackClientInterface } from "@elizaos/client-slack";
import { TelegramClientInterface } from "@elizaos/client-telegram";
import { TwitterClientInterface } from "@elizaos/client-twitter";
// import { ReclaimAdapter } from "@elizaos/plugin-reclaim";
import { DirectClient } from "@elizaos/client-direct";
import { PrimusAdapter } from "@elizaos/plugin-primus";

import {
AgentRuntime,
CacheManager,
Expand Down Expand Up @@ -98,6 +101,7 @@ import net from "net";
import path from "path";
import { fileURLToPath } from "url";
import yargs from "yargs";
import {dominosPlugin} from "@elizaos/plugin-dominos";

const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
const __dirname = path.dirname(__filename); // get the name of the directory
Expand Down Expand Up @@ -652,6 +656,19 @@ export async function createAgent(
elizaLogger.log("modelProvider", character.modelProvider);
elizaLogger.log("token", token);
}
if (
process.env.PRIMUS_APP_ID &&
process.env.PRIMUS_APP_SECRET &&
process.env.VERIFIABLE_INFERENCE_ENABLED === "true"){
verifiableInferenceAdapter = new PrimusAdapter({
appId: process.env.PRIMUS_APP_ID,
appSecret: process.env.PRIMUS_APP_SECRET,
attMode: "proxytls",
modelProvider: character.modelProvider,
token,
});
elizaLogger.log("Verifiable inference primus adapter initialized");
}

return new AgentRuntime({
databaseAdapter: db,
Expand Down
32 changes: 16 additions & 16 deletions packages/client-discord/src/actions/joinvoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
IAgentRuntime,
Memory,
State,
generateText,
ModelClass,
} from "@elizaos/core";
import {
Channel,
Expand All @@ -17,6 +19,7 @@ import {
Guild,
GuildMember,
} from "discord.js";
import { joinVoiceChannel } from "@discordjs/voice";

export default {
name: "JOIN_VOICE",
Expand Down Expand Up @@ -66,12 +69,7 @@ export default {
return false;
}

const client = state.discordClient as Client;

// Check if the client is connected to any voice channel
const isConnectedToVoice = client.voice.adapters.size === 0;

return isConnectedToVoice;
return true;
},
description: "Join a voice channel to participate in voice chat.",
handler: async (
Expand Down Expand Up @@ -115,31 +113,30 @@ export default {
);
});

if (!state.voiceManager) {
state.voiceManager = new VoiceManager({
client: state.discordClient,
runtime: runtime,
});
}

if (targetChannel) {
state.voiceManager.joinVoiceChannel({
joinVoiceChannel({
channelId: targetChannel.id,
guildId: (discordMessage as DiscordMessage).guild?.id as string,
adapterCreator: (client.guilds.cache.get(id) as Guild)
.voiceAdapterCreator,
selfDeaf: false,
selfMute: false,
group: client.user.id,
});
return true;
} else {
const member = (discordMessage as DiscordMessage)
.member as GuildMember;
if (member?.voice?.channel) {
state.voiceManager.joinVoiceChannel({
joinVoiceChannel({
channelId: member.voice.channel.id,
guildId: (discordMessage as DiscordMessage).guild
?.id as string,
adapterCreator: (client.guilds.cache.get(id) as Guild)
.voiceAdapterCreator,
selfDeaf: false,
selfMute: false,
group: client.user.id,
});
return true;
}
Expand Down Expand Up @@ -204,12 +201,15 @@ You should only respond with the name of the voice channel or none, no commentar
});

if (targetChannel) {
state.voiceManager.joinVoiceChannel({
joinVoiceChannel({
channelId: targetChannel.id,
guildId: (discordMessage as DiscordMessage).guild
?.id as string,
adapterCreator: (client.guilds.cache.get(id) as Guild)
.voiceAdapterCreator,
selfDeaf: false,
selfMute: false,
group: client.user.id,
});
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ export enum Clients {
LENS = "lens",
AUTO = "auto",
SLACK = "slack",
GITHUB = "github",
}

export interface IAgentConfig {
Expand Down Expand Up @@ -1479,7 +1480,9 @@ export interface ISlackService extends Service {
* Available verifiable inference providers
*/
export enum VerifiableInferenceProvider {
RECLAIM = "reclaim",
OPACITY = "opacity",
PRIMUS = "primus",
}

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-primus/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*

!dist/**
!package.json
!readme.md
!tsup.config.ts
208 changes: 208 additions & 0 deletions packages/plugin-primus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# @elizaos/plugin-primus

A plugin to fully verify agent activities, including LLM access, actions, and interactions with external providers,
powered by Primus' zkTLS protocol.

## Overview

In the Eliza framework, an agent consists of three key components: a brain (accessing an LLM), actions (the tasks the
agent performs), and perception (gathering external information from providers). To fully verify agent activities, it's
essential to ensure that the agent's thoughts, actions, and external information requests are all verifiable. This
plugin enables full verification of these activities.

The current plugin includes:

- Verification of inference from OpenAI's LLM.
- An example for verifying actions, such as posting a tweet (this can be extended to any other actions).
- An example to verify that the Bitcoin price is accurately fetched from Binance (this can be extended to any other data
providers).

## Usage
### LLM inference verification (PrimusAdapter)
`PrimusAdapter` implements `IVerifiableInferenceAdapter` and can be used as follows.
```typescript
import {PrimusAdapter} from "@elizaos/plugin-primus";
import {VerifiableInferenceOptions} from '@elizaos/core';

// Initialize primus adapter
const primusAdatper = new PrimusAdapter({
appId: process.env.PRIMUS_APP_ID,
appSecret: process.env.PRIMUS_APP_SECRET,
// Choose MPC-TLS or Proxy-TLS
attMode: "proxytls",
modelProvider: character.modelProvider,
token,
});

interface PrimusOptions {
appId: string;
appSecret: string;
attMode: string;
modelProvider?: ModelProviderName;
token?: string;
}

// The options for generating an attestation
const options: VerifiableInferenceOptions = {
// Optional: Override the default endpoint
endpoint: "https://api.openapi.com/chat/completions",
// Optional: Add custom headers
headers: {
"Content-Type": "application/json",
"Authorization": "bearer Token",
},
// Optional: Provider-specific options
providerOptions: {
temperature: 0.7,
},
};

// Generate an attestation for a network request.
const result = await primusAdapter.generateText(context, "gpt-4o", options);
// Verify the validity of the attestation.
const isValid = await primusAdapter.verifyProof(result.proof);
```

The core functions in `PrimusAdatper` are the following, which are also used in Actions and Providers.
```typescript
// Generate a zkTLS proof.
generateProof = async (
// The target endpoint of the network request.
endpoint: string,
// The HTTP method of the request, such as 'GET', 'POST', etc.
method: string,
// A record containing the headers of the request.
headers: Record<string, any>,
// The body of the request. It should be a string.
body: string,
//A [JSONPath](https://datatracker.ietf.org/doc/rfc9535/) expression to locate the specific field in the response you want to attest.
responseParsePath: string
): Promise<any>

// Verify the proof.
verifyProof = async (attestation: any): Promise<boolean>

```
### Verify the interaction with Providers
Here’s an example showcasing how to verify the validity of the BTC price retrieved from Binance. Developers can easily customize this process for other providers.
```typescript
const tokenPriceProvider: Provider = {
get: async (runtime: IAgentRuntime, message: Memory, _state?: State) => {
// Set the URL
const url = "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT";
const method = 'GET';
const headers = {
'Accept ': '*/*',
};
// Generate the proof
const attestation = await generateProof(url, method, headers, "", "$.price");
// Verify the proof.
const valid = await verifyProof(attestation);
if (!valid) {
throw new Error("Invalid price attestation");
}
......
},
};
```

### Verify the Actions
Below is an example showcasing how to post price information from the [tokenPriceProvider](./src/providers/tokenPriceProvider.ts) to Twitter. Developers can easily adapt this process for other providers.

Note that you need to configure the `.env` file correctly to post tweets.
```typescript
export const postTweetAction: Action = {
description: "Post a tweet on Twitter and be verified by Primus",
examples: [],
handler: async (
runtime: IAgentRuntime,
message: Memory,
state?: State
): Promise<boolean> => {
const contentYouWantToPost = await tokenPriceProvider.get(runtime, message, state);
const endpoint = 'https://twitter.com/i/api/graphql/a1p9RWpkYKBjWv_I3WzS-A/CreateTweet';
const method = 'POST';
const attestation = await generateProof(endpoint,method,headers,bodyStr,"$.data.create_tweet.tweet_results.result.rest_id");
elizaLogger.info(
"Tweet posting proof generated successfully:",
attestation
);
const verifyResult = verifyProof(attestation);
if (!verifyResult) {
throw new Error(
"Attestation verify failed, data from source is illegality"
);
}

},
name: "POST_TWEET",
similes: [],
validate: async (
runtime: IAgentRuntime,
message: Memory,
state?: State
) => {
const hasCredentials =
!!process.env.TWITTER_USERNAME && !!process.env.TWITTER_PASSWORD;
elizaLogger.log(`Has credentials: ${hasCredentials}`);

return hasCredentials;
},
};
```

## Installation

```bash
pnpm add @elizaos/plugin-primus
```

## Configuration

Add the following environment variables to your .env file:

```
PRIMUS_APP_ID=your_app_id
PRIMUS_APP_SECRET=your_app_secret
VERIFIABLE_INFERENCE_ENABLED=true
VERIFIABLE_INFERENCE_PROVIDER=primus
```

***How to get PRIMUS_APP_ID and PRIMUS_APP_SECRET***

1. Visit the [Primus Developer Hub](https://dev.primuslabs.xyz/).
2. Create a new project
3. Save your 'Application ID(PRIMUS_APP_ID)' and 'Secret Key(PRIMUS_APP_SECRET)'

To use the plugin, add `@elizaos/plugin-primus` to the plugins field in your character file. Here's an example of how your character file might look after the update:

```json
{
"name": "trump",
"modelProvider": "openai",
// just support openai now
"plugins": [
"@elizaos/plugin-primus"
],
// other fields
.....
}
```

## Run

```bash
# Start the server
pnpm start --characters="characters/xxx.character.json"
```

```bash
# Start the client
pnpm start:client
```

You can ask the agent: "Get the BTC price and tweet."

Loading

0 comments on commit 8d831fa

Please sign in to comment.