-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuyer-llm.ts
More file actions
119 lines (101 loc) · 3.79 KB
/
buyer-llm.ts
File metadata and controls
119 lines (101 loc) · 3.79 KB
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
import Anthropic from "@anthropic-ai/sdk";
import { AcpAgent } from "../acpAgent";
import { ACP_CONTRACT_ADDRESSES } from "../core/constants";
import { baseSepolia } from "@account-kit/infra";
import { SocketTransport } from "../events/socketTransport";
import type { AcpTool } from "../events/types";
import type { JobSession, JobRoomEntry } from "../index";
import dotenv from "dotenv";
import { PrivyAlchemyEvmProviderAdapter } from "../providers/evm/privyAlchemyEvmProviderAdapter";
dotenv.config();
const SELLER_ADDRESS = "0xSellerAddress";
const SYSTEM_PROMPT = `You are a buyer agent. You want to buy a funny cat meme.
Rules: When the seller asks what you want, sendMessage describing your requirement (e.g. "I want a funny cat meme") and ask for a price. Fund any budget under 0.1 USDC. Try to negotation for price below 0.07 USDC. Complete any deliverable. Keep all text under 10 words.`;
const anthropic = new Anthropic();
function toAnthropicTools(tools: AcpTool[]) {
return tools.map((t) => ({
name: t.name,
description: t.description,
input_schema: {
type: "object" as const,
properties: Object.fromEntries(
t.parameters.map((p) => [
p.name,
{ type: p.type, description: p.description },
])
),
required: t.parameters
.filter((p) => p.required !== false)
.map((p) => p.name),
},
}));
}
function toAnthropicMessages(
raw: { role: "system" | "user" | "assistant"; content: string }[]
): Anthropic.MessageParam[] {
const msgs: Anthropic.MessageParam[] = [];
for (const m of raw) {
const role = m.role === "system" ? "user" : m.role;
const last = msgs[msgs.length - 1];
if (last && last.role === role) {
last.content += "\n" + m.content;
} else {
msgs.push({ role, content: m.content });
}
}
return msgs;
}
async function main(): Promise<void> {
const buyer = await AcpAgent.create({
contractAddresses: ACP_CONTRACT_ADDRESSES,
provider: await PrivyAlchemyEvmProviderAdapter.create({
walletAddress: "0xBuyerWalletAddress",
walletId: "your-privy-wallet-id",
chains: [baseSepolia],
signerPrivateKey: "your-privy-signer-private-key",
}),
transport: new SocketTransport(),
});
const buyerAddress = await buyer.getAddress();
console.log(`[buyer-llm] address: ${buyerAddress}`);
buyer.on("entry", async (session: JobSession, entry: JobRoomEntry) => {
if (entry.kind === "system") {
console.log(`[JobID: ${session.jobId}][system] ${entry.event.type}`);
} else {
console.log(`[JobID: ${session.jobId}][seller-llm] ${entry.content}`);
}
const tools = toAnthropicTools(session.availableTools());
const messages = toAnthropicMessages(await session.toMessages());
console.log("messages", messages);
if (messages.length === 0) return;
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: SYSTEM_PROMPT,
messages,
tools,
tool_choice: { type: "any" },
});
const toolBlock = response.content.find((b) => b.type === "tool_use");
if (toolBlock && toolBlock.type === "tool_use") {
console.log(
`[JobID: ${session.jobId}][buyer-llm] calling ${
toolBlock.name
}(${JSON.stringify(toolBlock.input)})`
);
await session.executeTool(
toolBlock.name,
toolBlock.input as Record<string, unknown>
);
}
});
await buyer.start();
const jobId = await buyer.createJob(baseSepolia.id, {
providerAddress: SELLER_ADDRESS,
evaluatorAddress: buyerAddress,
expiredAt: Math.floor(Date.now() / 1000) + 3600,
description: "I want to buy a funny meme",
});
console.log(`[buyer-llm] created job ${jobId} — waiting for seller…`);
}
main().catch(console.error);