-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
159 lines (142 loc) · 5.23 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import { createInterface } from "node:readline";
import { openai } from "@ai-sdk/openai";
import { CoreMessage, generateText } from "ai";
// GOAT Plugins
import { getOnChainTools } from "@goat-sdk/adapter-vercel-ai";
import { crossmintHeadlessCheckout } from "@goat-sdk/plugin-crossmint-headless-checkout";
import { splToken, USDC } from "@goat-sdk/plugin-spl-token";
import { solana } from "@goat-sdk/wallet-solana";
import { Connection, Keypair } from "@solana/web3.js";
import base58 from "bs58";
import "dotenv/config";
const connection = new Connection(process.env.RPC_PROVIDER_URL as string);
const keypair = Keypair.fromSecretKey(base58.decode(process.env.WALLET_PRIVATE_KEY as string));
const apiKey = process.env.CROSSMINT_API_KEY;
if (!apiKey) {
throw new Error("CROSSMINT_API_KEY is not set");
}
interface ChatMessage {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
}
const conversationHistory: ChatMessage[] = [];
const rl = createInterface({
input: process.stdin,
output: process.stdout,
});
const getUserInput = () => {
return new Promise<string>((resolve) => {
rl.question("You: ", (input) => {
resolve(input);
});
});
};
(async () => {
try {
const tools = await getOnChainTools({
wallet: solana({ keypair, connection }),
plugins: [
splToken({ tokens: [USDC] }),
crossmintHeadlessCheckout({ apiKey: apiKey as string }),
],
});
console.clear();
console.log("👋 Welcome! How can I assist you with your shopping today?");
console.log("Type 'exit' to end the conversation.\n");
while (true) {
const userInput = await getUserInput();
if (userInput.toLowerCase() === 'exit') {
console.log("\n👋 Thanks for shopping with us! Have a great day!");
rl.close();
break;
}
conversationHistory.push({
role: 'user',
content: userInput,
id: `user-${Date.now()}`
});
const messages: CoreMessage[] = [
{
role: "system",
content: "Always ask for ALL required information in the first response: 1) name, 2) shipping address, 3) email address, 4) payment method (USDC, SOL, or ETH), and 5) preferred chain (EVM, Solana, or others). Only proceed with the purchase when all information is provided."
},
{
role: "system",
content: "When buying a product, prefer to use productLocator, i.e. 'amazon:B08SVZ775L', as the product locator.",
},
{
role: "system",
content: "When buying a product, extract the product locator from the product URL in the user's message.",
},
{
role: "system",
content: "When buying a product use the get_address tool to get the payment.payerAddress.",
},
{
role: "system",
content: "When buying a product, payment.payerAddress MUST be the address returned from the get_address tool.",
},
{
role: "system",
content: "When buying a product, require the user to provide a valid shipping address and email address.",
},
{
role: "system",
content: "When buying a product, payment.payerAddress MUST be a valid Solana public key.",
},
{
role: "system",
content: "When buying a product, parse the address provided and identify required fields for the order: address, city, state, zip, country.",
},
{
role: "system",
content: "When buying a product, use the checkout tool with recipient information (email and shipping address) rather than wallet addresses.",
},
{
role: "system",
content: "When a user provides their shipping address in the format 'Name, Street, City, State ZIP, Country', parse and store these details.",
},
{
role: "system",
content: "When buying a product on Solana chain, payment.payerAddress MUST be a base58-encoded Solana public key. Never use 0x-style addresses for Solana transactions."
},
{
role: "system",
content: "Always call get_address first and store its result. Use EXACTLY that address value for payment.payerAddress in subsequent operations. DO NOT use any other address, such as 3GectP9HqkXryazFmGXmQhfgAvmYyKkZBK3HKCwaqyTc."
},
{
role: "system",
content: "When an order is placed, don't ask me to confirm the payment to finalize the order.",
},
...conversationHistory.map(msg => ({
role: msg.role,
content: msg.content
}))
];
try {
const result = await generateText({
model: openai("gpt-4"),
tools: tools,
maxSteps: 10,
messages,
onStepFinish: (event) => {
console.log(event.toolResults);
}
});
conversationHistory.push({
role: 'assistant',
content: result.text,
id: `assistant-${Date.now()}`
});
console.log("\nAssistant:", result.text, "\n");
} catch (error) {
console.error('Error:', error);
}
}
} catch (error) {
console.error('Fatal error:', error);
rl.close();
process.exit(1);
}
})();