create-message-kit 1.1.7-beta.8 → 1.1.8-beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/README.md +2 -2
  2. package/index.js +62 -50
  3. package/package.json +1 -1
  4. package/templates/agent/src/handler/ens.ts +14 -12
  5. package/templates/agent/src/index.ts +4 -17
  6. package/templates/agent/src/prompt.ts +15 -32
  7. package/templates/agent/src/skills.ts +7 -20
  8. package/templates/gm/.env.example +1 -1
  9. package/templates/gm/src/index.ts +1 -5
  10. package/templates/group/.env.example +2 -3
  11. package/templates/group/src/handler/game.ts +4 -5
  12. package/templates/group/src/handler/helpers.ts +10 -6
  13. package/templates/group/src/handler/payment.ts +29 -0
  14. package/templates/group/src/handler/tipping.ts +17 -35
  15. package/templates/group/src/index.ts +17 -26
  16. package/templates/group/src/prompt.ts +28 -0
  17. package/templates/group/src/skills.ts +22 -107
  18. package/templates/agent/package.json +0 -22
  19. package/templates/agent/src/lib/gpt.ts +0 -161
  20. package/templates/agent/src/lib/openai.ts +0 -174
  21. package/templates/agent/src/lib/resolver.ts +0 -151
  22. package/templates/gm/package.json +0 -21
  23. package/templates/group/package.json +0 -23
  24. package/templates/group/src/handler/agent.ts +0 -67
  25. package/templates/group/src/handler/group.ts +0 -24
  26. package/templates/group/src/handler/loyalty.ts +0 -46
  27. package/templates/group/src/handler/splitpayment.ts +0 -65
  28. package/templates/group/src/handler/transaction.ts +0 -50
  29. package/templates/group/src/lib/gpt.ts +0 -161
  30. package/templates/group/src/lib/openai.ts +0 -174
  31. package/templates/group/src/lib/resolver.ts +0 -151
  32. package/templates/group/src/lib/stack.ts +0 -18
  33. package/templates/group/src/lib/vision.ts +0 -42
@@ -1,34 +1,25 @@
1
1
  import { run, HandlerContext } from "@xmtp/message-kit";
2
+ import { textGeneration, processMultilineResponse } from "@xmtp/message-kit";
3
+ import { agent_prompt } from "./prompt.js";
2
4
 
3
- // Main function to run the app
4
5
  run(async (context: HandlerContext) => {
5
6
  const {
6
- message: { typeId },
7
- group,
8
- } = context;
9
- switch (typeId) {
10
- case "reply":
11
- handleReply(context);
12
- break;
13
- }
14
- if (!group) {
15
- context.send("This is a group bot, add this address to a group");
16
- }
17
- });
18
- async function handleReply(context: HandlerContext) {
19
- const {
20
- v2client,
21
- getReplyChain,
22
- version,
23
7
  message: {
24
- content: { reference },
8
+ content: { text, params },
9
+ sender,
25
10
  },
26
11
  } = context;
27
12
 
28
- const { chain, isSenderInChain } = await getReplyChain(
29
- reference,
30
- version,
31
- v2client.address,
32
- );
33
- //await context.executeSkill(chain);
34
- }
13
+ try {
14
+ let userPrompt = params?.prompt ?? text;
15
+ const { reply } = await textGeneration(
16
+ sender.address,
17
+ userPrompt,
18
+ await agent_prompt(sender.address),
19
+ );
20
+ await processMultilineResponse(sender.address, reply, context);
21
+ } catch (error) {
22
+ console.error("Error during OpenAI call:", error);
23
+ await context.send("An error occurred while processing your request.");
24
+ }
25
+ });
@@ -0,0 +1,28 @@
1
+ import { skills } from "./skills.js";
2
+ import { defaultPromptTemplate } from "@xmtp/message-kit";
3
+
4
+ export async function agent_prompt(senderAddress: string) {
5
+ let fineTunedPrompt = `
6
+ ## Example response
7
+
8
+ 1. If user wants to play a game, use the skill 'game' and specify the game type.
9
+ Hey! Sure let's do that.\n/game wordle
10
+
11
+ 2. When user wants to pay a specific token:
12
+ I'll help you pay 1 USDC to 0x123...\n/pay 1 {TOKE}} 0x123456789...
13
+ *This will return a url to pay
14
+
15
+ 3. If the user wants to pay a eth domain:
16
+ I'll help you pay 1 USDC to vitalik.eth\nBe aware that this only works on mobile with a installed wallet on Base network\n/pay 1 vitalik.eth
17
+ *This will return a url to pay
18
+
19
+ 4. If the user wants to pay a username:
20
+ I'll help you pay 1 USDC to @fabri\nBe aware that this only works on mobile with a installed wallet on Base network\n/pay 1 @fabri
21
+ *This will return a url to pay
22
+
23
+ 5. If the user wants to play a game suggest direcly a game like wordle:
24
+ Let's play wordle!\n/game wordle
25
+ `;
26
+
27
+ return defaultPromptTemplate(fineTunedPrompt, senderAddress, skills, "@bot");
28
+ }
@@ -1,18 +1,17 @@
1
1
  import { handler as tipping } from "./handler/tipping.js";
2
- import { handler as agent } from "./handler/agent.js";
3
- import { handler as transaction } from "./handler/transaction.js";
2
+ import { handler as payment } from "./handler/payment.js";
4
3
  import { handler as games } from "./handler/game.js";
5
- import { handler as loyalty } from "./handler/loyalty.js";
6
- import { handler as groupHelp } from "./handler/helpers.js";
4
+ import { handler as help } from "./handler/helpers.js";
7
5
  import type { SkillGroup } from "@xmtp/message-kit";
8
6
 
9
7
  export const skills: SkillGroup[] = [
10
8
  {
11
- name: "Tipping",
12
- description: "Tip tokens via emoji, replies or command.",
9
+ name: "Group bot",
10
+ tag: "@bot",
11
+ description: "Group agent for tipping and transactions.",
13
12
  skills: [
14
13
  {
15
- command: "/tip [@usernames] [amount] [token]",
14
+ skill: "/tip [username] [amount] [token]",
16
15
  triggers: ["/tip"],
17
16
  examples: ["/tip @vitalik 10 usdc"],
18
17
  description: "Tip users in a specified token.",
@@ -27,22 +26,20 @@ export const skills: SkillGroup[] = [
27
26
  default: 10,
28
27
  type: "number",
29
28
  },
29
+ token: {
30
+ default: "usdc",
31
+ type: "string",
32
+ values: ["eth", "dai", "usdc", "degen"],
33
+ },
30
34
  },
31
35
  },
32
- ],
33
- },
34
-
35
- {
36
- name: "Transactions",
37
- description: "Multipurpose transaction frame built onbase.",
38
- skills: [
39
36
  {
40
- command: "/send [amount] [token] [username]",
41
- triggers: ["/send"],
42
- examples: ["/send 10 usdc @vitalik"],
37
+ skill: "/pay [amount] [token] [username]",
38
+ triggers: ["/pay"],
39
+ examples: ["/pay 10 usdc vitalik.eth", "/pay 1 @alix"],
43
40
  description:
44
41
  "Send a specified amount of a cryptocurrency to a destination address.",
45
- handler: transaction,
42
+ handler: payment,
46
43
  params: {
47
44
  amount: {
48
45
  default: 10,
@@ -60,47 +57,11 @@ export const skills: SkillGroup[] = [
60
57
  },
61
58
  },
62
59
  {
63
- command: "/swap [amount] [token_from] [token_to]",
64
- triggers: ["/swap"],
65
- examples: ["/swap 10 usdc eth"],
66
- description: "Exchange one type of cryptocurrency for another.",
67
- handler: transaction,
68
- params: {
69
- amount: {
70
- default: 10,
71
- type: "number",
72
- },
73
- token_from: {
74
- default: "usdc",
75
- type: "string",
76
- values: ["eth", "dai", "usdc", "degen"], // Accepted tokens
77
- },
78
- token_to: {
79
- default: "eth",
80
- type: "string",
81
- values: ["eth", "dai", "usdc", "degen"], // Accepted tokenss
82
- },
83
- },
84
- },
85
- {
86
- command: "/show",
87
- triggers: ["/show"],
88
- examples: ["/show"],
89
- handler: transaction,
90
- description: "Show the whole frame.",
91
- params: {},
92
- },
93
- ],
94
- },
95
- {
96
- name: "Games",
97
- description: "Provides various gaming experiences.",
98
- skills: [
99
- {
100
- command: "/game [game]",
60
+ skill: "/game [game]",
101
61
  triggers: ["/game", "🔎", "🔍"],
102
62
  handler: games,
103
63
  description: "Play a game.",
64
+ examples: ["/game wordle", "/game slot", "/game help"],
104
65
  params: {
105
66
  game: {
106
67
  default: "",
@@ -109,65 +70,19 @@ export const skills: SkillGroup[] = [
109
70
  },
110
71
  },
111
72
  },
112
- ],
113
- },
114
- {
115
- name: "Loyalty",
116
- description: "Manage group members and metadata.",
117
- skills: [
118
- {
119
- command: "/points",
120
- triggers: ["/points"],
121
- examples: ["/points"],
122
- handler: loyalty,
123
- description: "Check your points.",
124
- params: {},
125
- },
126
- {
127
- command: "/leaderboard",
128
- triggers: ["/leaderboard"],
129
- adminOnly: true,
130
- handler: loyalty,
131
- description: "Check the points of a user.",
132
- params: {},
133
- },
134
- ],
135
- },
136
- {
137
- name: "Agent",
138
- description: "Manage agent commands.",
139
- skills: [
140
- {
141
- command: "/agent [prompt]",
142
- triggers: ["/agent", "@agent", "@bot"],
143
- examples: ["/agent @vitalik"],
144
- handler: agent,
145
- description: "Manage agent commands.",
146
- params: {
147
- prompt: {
148
- default: "",
149
- type: "prompt",
150
- },
151
- },
152
- },
153
- ],
154
- },
155
- {
156
- name: "Help",
157
- description: "Get help with the bot.",
158
- skills: [
159
73
  {
160
- command: "/help",
74
+ skill: "/help",
161
75
  triggers: ["/help"],
162
76
  examples: ["/help"],
163
- handler: groupHelp,
77
+ handler: help,
164
78
  description: "Get help with the bot.",
165
79
  params: {},
166
80
  },
167
81
  {
168
- command: "/id",
82
+ skill: "/id",
169
83
  adminOnly: true,
170
- handler: groupHelp,
84
+ examples: ["/id"],
85
+ handler: help,
171
86
  triggers: ["/id"],
172
87
  description: "Get the group ID.",
173
88
  params: {},
@@ -1,22 +0,0 @@
1
- {
2
- "name": "agent",
3
- "private": true,
4
- "type": "module",
5
- "scripts": {
6
- "build": "tsc",
7
- "dev": "tsc -w & sleep 1 && nodemon --quiet dist/index.js",
8
- "start": "node dist/index.js"
9
- },
10
- "dependencies": {
11
- "@xmtp/message-kit": "workspace:*",
12
- "openai": "^4.65.0"
13
- },
14
- "devDependencies": {
15
- "@types/node": "^20.14.2",
16
- "nodemon": "^3.1.3",
17
- "typescript": "^5.4.5"
18
- },
19
- "engines": {
20
- "node": ">=20"
21
- }
22
- }
@@ -1,161 +0,0 @@
1
- import "dotenv/config";
2
- import type { SkillGroup } from "@xmtp/message-kit";
3
- import OpenAI from "openai";
4
- const openai = new OpenAI({
5
- apiKey: process.env.OPEN_AI_API_KEY,
6
- });
7
-
8
- type ChatHistoryEntry = { role: string; content: string };
9
- type ChatHistories = Record<string, ChatHistoryEntry[]>;
10
- // New ChatMemory class
11
- class ChatMemory {
12
- private histories: ChatHistories = {};
13
-
14
- getHistory(address: string): ChatHistoryEntry[] {
15
- return this.histories[address] || [];
16
- }
17
-
18
- addEntry(address: string, entry: ChatHistoryEntry) {
19
- if (!this.histories[address]) {
20
- this.histories[address] = [];
21
- }
22
- this.histories[address].push(entry);
23
- }
24
-
25
- initializeWithSystem(address: string, systemPrompt: string) {
26
- if (this.getHistory(address).length === 0) {
27
- this.addEntry(address, {
28
- role: "system",
29
- content: systemPrompt,
30
- });
31
- }
32
- }
33
-
34
- clear() {
35
- this.histories = {};
36
- }
37
- }
38
-
39
- // Create singleton instance
40
- export const chatMemory = new ChatMemory();
41
-
42
- export const clearMemory = () => {
43
- chatMemory.clear();
44
- };
45
-
46
- export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
47
- - You can respond with multiple messages if needed. Each message should be separated by a newline character.
48
- - You can trigger skills by only sending the command in a newline message.
49
- - Never announce actions without using a command separated by a newline character.
50
- - Dont answer in markdown format, just answer in plaintext.
51
- - Do not make guesses or assumptions
52
- - Only answer if the verified information is in the prompt.
53
- - Check that you are not missing a command
54
- - Focus only on helping users with operations detailed below.
55
- `;
56
-
57
- export function PROMPT_SKILLS_AND_EXAMPLES(skills: SkillGroup[], tag: string) {
58
- let foundSkills = skills.filter(
59
- (skill) => skill.tag == `@${tag.toLowerCase()}`,
60
- );
61
- if (!foundSkills.length || !foundSkills[0] || !foundSkills[0].skills)
62
- return "";
63
- let returnPrompt = `\nCommands:\n${foundSkills[0].skills
64
- .map((skill) => skill.command)
65
- .join("\n")}\n\nExamples:\n${foundSkills[0].skills
66
- .map((skill) => skill.examples)
67
- .join("\n")}`;
68
- return returnPrompt;
69
- }
70
-
71
- export async function textGeneration(
72
- memoryKey: string,
73
- userPrompt: string,
74
- systemPrompt: string,
75
- ) {
76
- if (!memoryKey) {
77
- clearMemory();
78
- }
79
- let messages = chatMemory.getHistory(memoryKey);
80
- chatMemory.initializeWithSystem(memoryKey, systemPrompt);
81
- if (messages.length === 0) {
82
- messages.push({
83
- role: "system",
84
- content: systemPrompt,
85
- });
86
- }
87
- messages.push({
88
- role: "user",
89
- content: userPrompt,
90
- });
91
- try {
92
- const response = await openai.chat.completions.create({
93
- model: "gpt-4o",
94
- messages: messages as any,
95
- });
96
- const reply = response.choices[0].message.content;
97
- messages.push({
98
- role: "assistant",
99
- content: reply || "No response from OpenAI.",
100
- });
101
- const cleanedReply = parseMarkdown(reply as string);
102
- chatMemory.addEntry(memoryKey, {
103
- role: "assistant",
104
- content: cleanedReply,
105
- });
106
- return { reply: cleanedReply, history: messages };
107
- } catch (error) {
108
- console.error("Failed to fetch from OpenAI:", error);
109
- throw error;
110
- }
111
- }
112
-
113
- export async function processMultilineResponse(
114
- memoryKey: string,
115
- reply: string,
116
- context: any,
117
- ) {
118
- if (!memoryKey) {
119
- clearMemory();
120
- }
121
- let messages = reply
122
- .split("\n")
123
- .map((message: string) => parseMarkdown(message))
124
- .filter((message): message is string => message.length > 0);
125
-
126
- console.log(messages);
127
- for (const message of messages) {
128
- if (message.startsWith("/")) {
129
- const response = await context.executeSkill(message);
130
- if (response && typeof response.message === "string") {
131
- let msg = parseMarkdown(response.message);
132
- chatMemory.addEntry(memoryKey, {
133
- role: "system",
134
- content: msg,
135
- });
136
- await context.send(response.message);
137
- }
138
- } else {
139
- await context.send(message);
140
- }
141
- }
142
- }
143
- export function parseMarkdown(message: string) {
144
- let trimmedMessage = message;
145
- // Remove bold and underline markdown
146
- trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
147
- // Remove markdown links, keeping only the URL
148
- trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
149
- // Remove markdown headers
150
- trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
151
- // Remove inline code formatting
152
- trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
153
- // Remove single backticks at the start or end of the message
154
- trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
155
- // Remove leading and trailing whitespace
156
- trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
157
- // Remove any remaining leading or trailing whitespace
158
- trimmedMessage = trimmedMessage.trim();
159
-
160
- return trimmedMessage;
161
- }
@@ -1,174 +0,0 @@
1
- import dotenv from "dotenv";
2
- dotenv.config();
3
- import type { SkillGroup } from "@xmtp/message-kit";
4
- import OpenAI from "openai";
5
- const openai = new OpenAI({
6
- apiKey: process.env.OPEN_AI_API_KEY,
7
- });
8
-
9
- export type ChatHistoryEntry = { role: string; content: string };
10
- export type ChatHistories = Record<string, ChatHistoryEntry[]>;
11
-
12
- // New ChatMemory class
13
- class ChatMemory {
14
- private histories: ChatHistories = {};
15
-
16
- getHistory(address: string): ChatHistoryEntry[] {
17
- return this.histories[address] || [];
18
- }
19
-
20
- addEntry(address: string, entry: ChatHistoryEntry) {
21
- if (!this.histories[address]) {
22
- this.histories[address] = [];
23
- }
24
- this.histories[address].push(entry);
25
- }
26
-
27
- initializeWithSystem(address: string, systemPrompt: string) {
28
- if (this.getHistory(address).length === 0) {
29
- this.addEntry(address, {
30
- role: "system",
31
- content: systemPrompt,
32
- });
33
- }
34
- }
35
-
36
- clear() {
37
- this.histories = {};
38
- }
39
- }
40
-
41
- export const clearMemory = () => {
42
- chatHistories = {};
43
- };
44
-
45
- // Create singleton instance
46
- export const chatMemory = new ChatMemory();
47
-
48
- let chatHistories: ChatHistories = {};
49
- export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
50
- - You can respond with multiple messages if needed. Each message should be separated by a newline character.
51
- - You can trigger skills by only sending the command in a newline message.
52
- - Never announce actions without using a command separated by a newline character.
53
- - Dont answer in markdown format, just answer in plaintext.
54
- - Do not make guesses or assumptions
55
- - Only answer if the verified information is in the prompt.
56
- - Check that you are not missing a command
57
- - Focus only on helping users with operations detailed below.
58
- `;
59
-
60
- export const PROMPT_SKILLS_AND_EXAMPLES = (skills: SkillGroup[]) => `
61
- Commands:
62
- ${skills
63
- .map((skill) => skill.skills.map((s) => s.command).join("\n"))
64
- .join("\n")}
65
-
66
- Examples:
67
- ${skills
68
- .map((skill) => skill.skills.map((s) => s.examples).join("\n"))
69
- .join("\n")}
70
- `;
71
-
72
- export async function agentResponse(
73
- sender: { address: string },
74
- userPrompt: string,
75
- systemPrompt: string,
76
- context: any,
77
- ) {
78
- try {
79
- const { reply } = await textGeneration(
80
- sender.address,
81
- userPrompt,
82
- systemPrompt,
83
- );
84
- await processMultilineResponse(sender.address, reply, context);
85
- } catch (error) {
86
- console.error("Error during OpenAI call:", error);
87
- await context.reply("An error occurred while processing your request.");
88
- }
89
- }
90
- export async function textGeneration(
91
- address: string,
92
- userPrompt: string,
93
- systemPrompt: string,
94
- ) {
95
- let messages = chatMemory.getHistory(address);
96
- chatMemory.initializeWithSystem(address, systemPrompt);
97
- if (messages.length === 0) {
98
- messages.push({
99
- role: "system",
100
- content: systemPrompt,
101
- });
102
- }
103
- messages.push({
104
- role: "user",
105
- content: userPrompt,
106
- });
107
- try {
108
- const response = await openai.chat.completions.create({
109
- model: "gpt-4o",
110
- messages: messages as any,
111
- });
112
- const reply = response.choices[0].message.content;
113
- messages.push({
114
- role: "assistant",
115
- content: reply || "No response from OpenAI.",
116
- });
117
- const cleanedReply = parseMarkdown(reply as string);
118
- chatMemory.addEntry(address, {
119
- role: "assistant",
120
- content: cleanedReply,
121
- });
122
- return { reply: cleanedReply, history: messages };
123
- } catch (error) {
124
- console.error("Failed to fetch from OpenAI:", error);
125
- throw error;
126
- }
127
- }
128
-
129
- export async function processMultilineResponse(
130
- address: string,
131
- reply: string,
132
- context: any,
133
- ) {
134
- let messages = reply
135
- .split("\n")
136
- .map((message: string) => parseMarkdown(message))
137
- .filter((message): message is string => message.length > 0);
138
-
139
- console.log(messages);
140
- for (const message of messages) {
141
- if (message.startsWith("/")) {
142
- const response = await context.skill(message);
143
- if (response && typeof response.message === "string") {
144
- let msg = parseMarkdown(response.message);
145
- chatMemory.addEntry(address, {
146
- role: "system",
147
- content: msg,
148
- });
149
- await context.send(response.message);
150
- }
151
- } else {
152
- await context.send(message);
153
- }
154
- }
155
- }
156
- export function parseMarkdown(message: string) {
157
- let trimmedMessage = message;
158
- // Remove bold and underline markdown
159
- trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
160
- // Remove markdown links, keeping only the URL
161
- trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
162
- // Remove markdown headers
163
- trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
164
- // Remove inline code formatting
165
- trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
166
- // Remove single backticks at the start or end of the message
167
- trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
168
- // Remove leading and trailing whitespace
169
- trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
170
- // Remove any remaining leading or trailing whitespace
171
- trimmedMessage = trimmedMessage.trim();
172
-
173
- return trimmedMessage;
174
- }