acp-plugin-gamesdk 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ from datetime import datetime, timedelta
2
2
  from typing import List, Optional
3
3
  from web3 import Web3
4
4
  import requests
5
- from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpState
5
+ from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpOffering, AcpState
6
6
  from acp_plugin_gamesdk.acp_token import AcpToken, MemoType
7
7
  import time
8
8
 
@@ -29,7 +29,6 @@ class AcpClient:
29
29
  def browse_agents(self, cluster: Optional[str] = None, query: Optional[str] = None) -> List[AcpAgent]:
30
30
  url = f"{self.acp_base_url}/agents"
31
31
 
32
- # Build URL with query parameters
33
32
  if query:
34
33
  url += f"?search={requests.utils.quote(query)}"
35
34
 
@@ -48,25 +47,32 @@ class AcpClient:
48
47
  result = []
49
48
 
50
49
  for agent in response_json.get("data", []):
50
+ if agent["offerings"]:
51
+ offerings = [AcpOffering(name=offering["name"], price=offering["price"]) for offering in agent["offerings"]]
52
+ else:
53
+ offerings = None
54
+
51
55
  result.append(
52
- {
53
- "id": agent["id"],
54
- "name": agent["name"],
55
- "description": agent["description"],
56
- "walletAddress": agent["walletAddress"]
57
- }
56
+ AcpAgent(
57
+ id=agent["id"],
58
+ name=agent["name"],
59
+ description=agent["description"],
60
+ wallet_address=agent["walletAddress"],
61
+ offerings=offerings
62
+ )
58
63
  )
59
64
 
60
65
  return result
61
66
 
62
- def create_job(self, provider_address: str, price: float, job_description: str) -> int:
67
+ def create_job(self, provider_address: str, price: float, job_description: str, evaluator_address: str) -> int:
63
68
  expire_at = datetime.now() + timedelta(days=1)
64
69
 
65
70
  tx_result = self.acp_token.create_job(
66
71
  provider_address=provider_address,
67
- evaluator_address=provider_address,
72
+ evaluator_address=evaluator_address,
68
73
  expire_at=expire_at
69
74
  )
75
+
70
76
  job_id = None
71
77
  retry_count = 3
72
78
  retry_delay = 3
@@ -86,7 +92,7 @@ class AcpClient:
86
92
  break
87
93
 
88
94
  if (data.get("status") == "success"):
89
- job_id = data.get("result").get("jobId")
95
+ job_id = int(data.get("result").get("jobId"))
90
96
 
91
97
  if (job_id is not None and job_id != ""):
92
98
  break
@@ -102,7 +108,7 @@ class AcpClient:
102
108
  raise Exception("Failed to create job")
103
109
 
104
110
  self.acp_token.create_memo(
105
- job_id=int(job_id),
111
+ job_id=job_id,
106
112
  content=job_description,
107
113
  memo_type=MemoType.MESSAGE,
108
114
  is_secured=False,
@@ -110,12 +116,13 @@ class AcpClient:
110
116
  )
111
117
 
112
118
  payload = {
113
- "jobId": int(job_id),
119
+ "jobId": job_id,
114
120
  "clientAddress": self.agent_wallet_address,
115
121
  "providerAddress": provider_address,
116
122
  "description": job_description,
117
123
  "price": price,
118
- "expiredAt": expire_at.isoformat()
124
+ "expiredAt": expire_at.isoformat(),
125
+ "evaluatorAddress": evaluator_address
119
126
  }
120
127
 
121
128
  requests.post(
@@ -133,11 +140,10 @@ class AcpClient:
133
140
  def response_job(self, job_id: int, accept: bool, memo_id: int, reasoning: str):
134
141
  if accept:
135
142
  self.acp_token.sign_memo(memo_id, accept, reasoning)
136
-
137
143
  time.sleep(5)
138
144
 
139
145
  return self.acp_token.create_memo(
140
- job_id=int(job_id),
146
+ job_id=job_id,
141
147
  content=f"Job {job_id} accepted. {reasoning}",
142
148
  memo_type=MemoType.MESSAGE,
143
149
  is_secured=False,
@@ -145,7 +151,7 @@ class AcpClient:
145
151
  )
146
152
  else:
147
153
  return self.acp_token.create_memo(
148
- job_id=int(job_id),
154
+ job_id=job_id,
149
155
  content=f"Job {job_id} rejected. {reasoning}",
150
156
  memo_type=MemoType.MESSAGE,
151
157
  is_secured=False,
@@ -162,9 +168,9 @@ class AcpClient:
162
168
  time.sleep(5)
163
169
  return self.acp_token.sign_memo(memo_id, True, reason)
164
170
 
165
- def deliver_job(self, job_id: int, deliverable: str, memo_id: int, reason: str):
171
+ def deliver_job(self, job_id: int, deliverable: str):
166
172
  return self.acp_token.create_memo(
167
- job_id=int(job_id),
173
+ job_id=job_id,
168
174
  content=deliverable,
169
175
  memo_type=MemoType.MESSAGE,
170
176
  is_secured=False,
@@ -172,17 +178,6 @@ class AcpClient:
172
178
  )
173
179
 
174
180
  def add_tweet(self, job_id: int, tweet_id: str, content: str):
175
- """
176
- Add a tweet to a job.
177
-
178
- Args:
179
- job_id: The ID of the job
180
- tweet_id: The ID of the tweet
181
- content: The content of the tweet
182
-
183
- Raises:
184
- Exception: If the request fails
185
- """
186
181
  payload = {
187
182
  "tweetId": tweet_id,
188
183
  "content": content
@@ -204,14 +199,9 @@ class AcpClient:
204
199
 
205
200
  return response.json()
206
201
 
207
- def reset_state(self, wallet_address: str ) -> None:
208
- if not wallet_address:
209
- raise Exception("Wallet address is required")
210
-
211
- address = wallet_address
212
-
202
+ def reset_state(self) -> None:
213
203
  response = requests.delete(
214
- f"{self.base_url}/states/{address}",
204
+ f"{self.base_url}/states/{self.agent_wallet_address}",
215
205
  headers={"x-api-key": self.api_key}
216
206
  )
217
207
 
@@ -1,15 +1,21 @@
1
+ from collections.abc import Callable
2
+ import signal
3
+ import sys
1
4
  from typing import List, Dict, Any, Optional,Tuple
2
5
  import json
3
6
  from dataclasses import dataclass
4
7
  from datetime import datetime
5
8
 
9
+ import socketio
10
+ import socketio.client
11
+
6
12
  from game_sdk.game.agent import WorkerConfig
7
- from game_sdk.game.custom_types import Function, FunctionResultStatus
13
+ from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus
8
14
  from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
9
15
  from twitter_plugin_gamesdk.game_twitter_plugin import GameTwitterPlugin
10
16
  from acp_plugin_gamesdk.acp_client import AcpClient
11
17
  from acp_plugin_gamesdk.acp_token import AcpToken
12
- from acp_plugin_gamesdk.interface import AcpJobPhasesDesc, IInventory
18
+ from acp_plugin_gamesdk.interface import AcpJobPhasesDesc, IDeliverable, IInventory
13
19
 
14
20
  @dataclass
15
21
  class AcpPluginOptions:
@@ -17,13 +23,21 @@ class AcpPluginOptions:
17
23
  acp_token_client: AcpToken
18
24
  twitter_plugin: TwitterPlugin | GameTwitterPlugin = None
19
25
  cluster: Optional[str] = None
20
- acp_base_url: Optional[str] = None
26
+ evaluator_cluster: Optional[str] = None
27
+ on_evaluate: Optional[Callable[[IDeliverable], Tuple[bool, str]]] = None
21
28
 
29
+ SocketEvents = {
30
+ "JOIN_EVALUATOR_ROOM": "joinEvaluatorRoom",
31
+ "LEAVE_EVALUATOR_ROOM": "leaveEvaluatorRoom",
32
+ "ON_EVALUATE": "onEvaluate",
33
+ "ROOM_JOINED" : "roomJoined"
34
+ }
22
35
 
23
36
  class AcpPlugin:
24
37
  def __init__(self, options: AcpPluginOptions):
25
38
  print("Initializing AcpPlugin")
26
- self.acp_client = AcpClient(options.api_key, options.acp_token_client, options.acp_base_url)
39
+ self.acp_token_client = options.acp_token_client
40
+ self.acp_client = AcpClient(options.api_key, options.acp_token_client, options.acp_token_client.acp_base_url)
27
41
  self.id = "acp_worker"
28
42
  self.name = "ACP Worker"
29
43
  self.description = """
@@ -42,16 +56,76 @@ class AcpPlugin:
42
56
  NOTE: This is NOT for finding clients - only for executing trades when there's a specific need to buy or sell something.
43
57
  """
44
58
  self.cluster = options.cluster
59
+ self.evaluator_cluster = options.evaluator_cluster
45
60
  self.twitter_plugin = options.twitter_plugin
46
61
  self.produced_inventory: List[IInventory] = []
47
- self.acp_base_url = options.acp_base_url
48
-
62
+ self.acp_base_url = self.acp_token_client.acp_base_url if self.acp_token_client.acp_base_url is None else "https://acpx-staging.virtuals.io/api"
63
+ if (options.on_evaluate is not None):
64
+ print("Initializing socket")
65
+ self.on_evaluate = options.on_evaluate
66
+ self.socket = None
67
+ self.initializeSocket()
68
+
69
+ def initializeSocket(self) -> Tuple[bool, str]:
70
+ """
71
+ Initialize socket connection for real-time communication.
72
+ Returns a tuple of (success, message).
73
+ """
74
+ try:
75
+ print("Initializing socket after")
76
+ self.socket = socketio.Client()
77
+
78
+ # Set up authentication before connecting
79
+ self.socket.auth = {
80
+ "evaluatorAddress": self.acp_token_client.agent_wallet_address
81
+ }
82
+
83
+ # Connect socket to GAME SDK dev server
84
+ self.socket.connect("https://sdk-dev.game.virtuals.io", auth=self.socket.auth)
85
+
86
+ if (self.socket.connected):
87
+ self.socket.emit(SocketEvents["JOIN_EVALUATOR_ROOM"], self.acp_token_client.agent_wallet_address)
88
+
89
+
90
+ # Set up event handler for evaluation requests
91
+ @self.socket.on(SocketEvents["ON_EVALUATE"])
92
+ def on_evaluate(data):
93
+ if self.on_evaluate:
94
+ deliverable = data.get("deliverable")
95
+ memo_id = data.get("memoId")
96
+
97
+ is_approved, reasoning = self.on_evaluate(deliverable)
98
+
99
+ self.acp_token_client.sign_memo(memo_id, is_approved, reasoning)
100
+
101
+ # Set up cleanup function for graceful shutdown
102
+ def cleanup():
103
+ if self.socket:
104
+ print("Disconnecting socket")
105
+
106
+ import time
107
+ time.sleep(1)
108
+ self.socket.disconnect()
109
+
110
+ def signal_handler(sig, frame):
111
+ cleanup()
112
+ sys.exit(0)
113
+
114
+ signal.signal(signal.SIGINT, signal_handler)
115
+ signal.signal(signal.SIGTERM, signal_handler)
116
+
117
+ return True, "Socket initialized successfully"
118
+
119
+ except Exception as e:
120
+ return False, f"Failed to initialize socket: {str(e)}"
121
+
122
+
49
123
 
50
124
  def add_produce_item(self, item: IInventory) -> None:
51
125
  self.produced_inventory.append(item)
52
126
 
53
127
  def reset_state(self) -> None:
54
- self.acp_client.reset_state(self.acp_client.agent_wallet_address)
128
+ self.acp_client.reset_state()
55
129
 
56
130
  def get_acp_state(self) -> Dict:
57
131
  server_state = self.acp_client.get_state()
@@ -74,7 +148,7 @@ class AcpPlugin:
74
148
  **(self.get_acp_state()),
75
149
  }
76
150
 
77
- data = WorkerConfig(
151
+ worker_config = WorkerConfig(
78
152
  id=self.id,
79
153
  worker_description=self.description,
80
154
  action_space=functions,
@@ -82,7 +156,7 @@ class AcpPlugin:
82
156
  instruction=data.get("instructions") if data else None
83
157
  )
84
158
 
85
- return data
159
+ return worker_config
86
160
 
87
161
  @property
88
162
  def agent_description(self) -> str:
@@ -109,9 +183,9 @@ class AcpPlugin:
109
183
 
110
184
  if not agents:
111
185
  return FunctionResultStatus.FAILED, "No other trading agents found in the system. Please try again later when more agents are available.", {}
112
-
186
+
113
187
  return FunctionResultStatus.DONE, json.dumps({
114
- "availableAgents": agents,
188
+ "availableAgents": [{"id": agent.id, "name": agent.name, "description": agent.description, "wallet_address": agent.wallet_address, "offerings": [{"name": offering.name, "price": offering.price} for offering in agent.offerings] if agent.offerings else []} for agent in agents],
115
189
  "totalAgentsFound": len(agents),
116
190
  "timestamp": datetime.now().timestamp(),
117
191
  "note": "Use the walletAddress when initiating a job with your chosen trading partner."
@@ -119,74 +193,115 @@ class AcpPlugin:
119
193
 
120
194
  @property
121
195
  def search_agents_functions(self) -> Function:
196
+ reasoning_arg = Argument(
197
+ name="reasoning",
198
+ type="string",
199
+ description="Explain why you need to find trading partners at this time",
200
+ )
201
+
202
+ keyword_arg = Argument(
203
+ name="keyword",
204
+ type="string",
205
+ description="Search for agents by name or description. Use this to find specific trading partners or products.",
206
+ )
207
+
122
208
  return Function(
123
209
  fn_name="search_agents",
124
210
  fn_description="Get a list of all available trading agents and what they're selling. Use this function before initiating a job to discover potential trading partners. Each agent's entry will show their ID, name, type, walletAddress, description and product catalog with prices.",
125
- args=[
126
- {
127
- "name": "reasoning",
128
- "type": "string",
129
- "description": "Explain why you need to find trading partners at this time",
130
- },
131
- {
132
- "name": "keyword",
133
- "type": "string",
134
- "description": "Search for agents by name or description. Use this to find specific trading partners or products.",
135
- },
136
- ],
211
+ args=[reasoning_arg, keyword_arg],
137
212
  executable=self._search_agents_executable
138
213
  )
139
214
 
140
215
  @property
141
216
  def initiate_job(self) -> Function:
217
+ seller_wallet_address_arg = Argument(
218
+ name="sellerWalletAddress",
219
+ type="string",
220
+ description="The seller's agent wallet address you want to buy from",
221
+ )
222
+
223
+ price_arg = Argument(
224
+ name="price",
225
+ type="string",
226
+ description="Offered price for service",
227
+ )
228
+
229
+ reasoning_arg = Argument(
230
+ name="reasoning",
231
+ type="string",
232
+ description="Why you are making this purchase request",
233
+ )
234
+
235
+ service_requirements_arg = Argument(
236
+ name="serviceRequirements",
237
+ type="string",
238
+ description="Detailed specifications for service-based items",
239
+ )
240
+
241
+ require_evaluation_arg = Argument(
242
+ name="requireEvaluation",
243
+ type="boolean",
244
+ description="Decide if your job request is complex enough to spend money for evaluator agent to assess the relevancy of the output. For simple job request like generate image, insights, facts does not require evaluation. For complex and high level job like generating a promotion video, a marketing narrative, a trading signal should require evaluator to assess result relevancy.",
245
+ )
246
+
247
+ evaluator_keyword_arg = Argument(
248
+ name="evaluatorKeyword",
249
+ type="string",
250
+ description="Keyword to search for a evaluator",
251
+ )
252
+
253
+ args = [seller_wallet_address_arg, price_arg, reasoning_arg, service_requirements_arg, require_evaluation_arg, evaluator_keyword_arg]
254
+
255
+ if self.twitter_plugin is not None:
256
+ tweet_content_arg = Argument(
257
+ name="tweetContent",
258
+ type="string",
259
+ description="Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
260
+ )
261
+ args.append(tweet_content_arg)
262
+
142
263
  return Function(
143
264
  fn_name="initiate_job",
144
265
  fn_description="Creates a purchase request for items from another agent's catalog. Only for use when YOU are the buyer. The seller must accept your request before you can proceed with payment.",
145
- args=[
146
- {
147
- "name": "sellerWalletAddress",
148
- "type": "string",
149
- "description": "The seller's agent wallet address you want to buy from",
150
- },
151
- {
152
- "name": "price",
153
- "type": "string",
154
- "description": "Offered price for service",
155
- },
156
- {
157
- "name": "reasoning",
158
- "type": "string",
159
- "description": "Why you are making this purchase request",
160
- },
161
- {
162
- "name": "serviceRequirements",
163
- "type": "string",
164
- "description": "Detailed specifications for service-based items",
165
- },
166
- {
167
- "name": "tweetContent",
168
- "type": "string",
169
- "description": "Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
170
- },
171
- ],
266
+ args=args,
172
267
  executable=self._initiate_job_executable
173
268
  )
174
269
 
175
- def _initiate_job_executable(self, sellerWalletAddress: str, price: str, reasoning: str, serviceRequirements: str, tweetContent : str) -> Tuple[FunctionResultStatus, str, dict]:
270
+ def _initiate_job_executable(self, sellerWalletAddress: str, price: str, reasoning: str, serviceRequirements: str, requireEvaluation: bool, evaluatorKeyword: str, tweetContent: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
176
271
  if not price:
177
272
  return FunctionResultStatus.FAILED, "Missing price - specify how much you're offering per unit", {}
178
-
273
+
274
+ if not reasoning:
275
+ return FunctionResultStatus.FAILED, "Missing reasoning - explain why you're making this purchase request", {}
276
+
179
277
  try:
180
278
  state = self.get_acp_state()
181
279
 
182
280
  if state["jobs"]["active"]["asABuyer"]:
183
281
  return FunctionResultStatus.FAILED, "You already have an active job as a buyer", {}
184
-
282
+
283
+ if not sellerWalletAddress:
284
+ return FunctionResultStatus.FAILED, "Missing seller wallet address - specify the agent you want to buy from", {}
285
+
286
+ if bool(requireEvaluation) and not evaluatorKeyword:
287
+ return FunctionResultStatus.FAILED, "Missing validator keyword - provide a keyword to search for a validator", {}
288
+
289
+ evaluatorAddress = self.acp_token_client.get_agent_wallet_address()
290
+
291
+ if bool(requireEvaluation):
292
+ validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluatorKeyword)
293
+
294
+ if len(validators) == 0:
295
+ return FunctionResultStatus.FAILED, "No evaluator found - try a different keyword", {}
296
+
297
+ evaluatorAddress = validators[0].wallet_address
298
+
185
299
  # ... Rest of validation logic ...
186
300
  job_id = self.acp_client.create_job(
187
301
  sellerWalletAddress,
188
302
  float(price),
189
- serviceRequirements
303
+ serviceRequirements,
304
+ evaluatorAddress
190
305
  )
191
306
 
192
307
  if (self.twitter_plugin is not None and tweetContent is not None):
@@ -208,35 +323,42 @@ class AcpPlugin:
208
323
 
209
324
  @property
210
325
  def respond_job(self) -> Function:
326
+ job_id_arg = Argument(
327
+ name="jobId",
328
+ type="integer",
329
+ description="The job ID you are responding to",
330
+ )
331
+
332
+ decision_arg = Argument(
333
+ name="decision",
334
+ type="string",
335
+ description="Your response: 'ACCEPT' or 'REJECT'",
336
+ )
337
+
338
+ reasoning_arg = Argument(
339
+ name="reasoning",
340
+ type="string",
341
+ description="Why you made this decision",
342
+ )
343
+
344
+ args = [job_id_arg, decision_arg, reasoning_arg]
345
+
346
+ if self.twitter_plugin is not None:
347
+ tweet_content_arg = Argument(
348
+ name="tweetContent",
349
+ type="string",
350
+ description="Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
351
+ )
352
+ args.append(tweet_content_arg)
353
+
211
354
  return Function(
212
355
  fn_name="respond_to_job",
213
356
  fn_description="Accepts or rejects an incoming 'request' job",
214
- args=[
215
- {
216
- "name": "jobId",
217
- "type": "string",
218
- "description": "The job ID you are responding to",
219
- },
220
- {
221
- "name": "decision",
222
- "type": "string",
223
- "description": "Your response: 'ACCEPT' or 'REJECT'",
224
- },
225
- {
226
- "name": "reasoning",
227
- "type": "string",
228
- "description": "Why you made this decision",
229
- },
230
- {
231
- "name": "tweetContent",
232
- "type": "string",
233
- "description": "Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
234
- },
235
- ],
357
+ args=args,
236
358
  executable=self._respond_job_executable
237
359
  )
238
360
 
239
- def _respond_job_executable(self, jobId: str, decision: str, reasoning: str, tweetContent: str) -> Tuple[FunctionResultStatus, str, dict]:
361
+ def _respond_job_executable(self, jobId: int, decision: str, reasoning: str, tweetContent: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
240
362
  if not jobId:
241
363
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're responding to", {}
242
364
 
@@ -250,7 +372,7 @@ class AcpPlugin:
250
372
  state = self.get_acp_state()
251
373
 
252
374
  job = next(
253
- (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == int(jobId)),
375
+ (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == jobId),
254
376
  None
255
377
  )
256
378
 
@@ -261,13 +383,13 @@ class AcpPlugin:
261
383
  return FunctionResultStatus.FAILED, f"Cannot respond - job is in '{job['phase']}' phase, must be in 'request' phase", {}
262
384
 
263
385
  self.acp_client.response_job(
264
- int(jobId),
386
+ jobId,
265
387
  decision == "ACCEPT",
266
388
  job["memo"][0]["id"],
267
389
  reasoning
268
390
  )
269
391
 
270
- if (self.twitter_plugin is not None):
392
+ if (self.twitter_plugin is not None and tweetContent is not None):
271
393
  tweet_history = job.get("tweetHistory", [])
272
394
  tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
273
395
  if (tweet_id is not None):
@@ -287,35 +409,42 @@ class AcpPlugin:
287
409
 
288
410
  @property
289
411
  def pay_job(self) -> Function:
412
+ job_id_arg = Argument(
413
+ name="jobId",
414
+ type="integer",
415
+ description="The job ID you are paying for",
416
+ )
417
+
418
+ amount_arg = Argument(
419
+ name="amount",
420
+ type="float",
421
+ description="The total amount to pay", # in Ether
422
+ )
423
+
424
+ reasoning_arg = Argument(
425
+ name="reasoning",
426
+ type="string",
427
+ description="Why you are making this payment",
428
+ )
429
+
430
+ args = [job_id_arg, amount_arg, reasoning_arg]
431
+
432
+ if self.twitter_plugin is not None:
433
+ tweet_content_arg = Argument(
434
+ name="tweetContent",
435
+ type="string",
436
+ description="Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
437
+ )
438
+ args.append(tweet_content_arg)
439
+
290
440
  return Function(
291
441
  fn_name="pay_job",
292
442
  fn_description="Processes payment for an accepted purchase request",
293
- args=[
294
- {
295
- "name": "jobId",
296
- "type": "number",
297
- "description": "The job ID you are paying for",
298
- },
299
- {
300
- "name": "amount",
301
- "type": "number",
302
- "description": "The total amount to pay",
303
- },
304
- {
305
- "name": "reasoning",
306
- "type": "string",
307
- "description": "Why you are making this payment",
308
- },
309
- {
310
- "name": "tweetContent",
311
- "type": "string",
312
- "description": "Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
313
- },
314
- ],
443
+ args=args,
315
444
  executable=self._pay_job_executable
316
445
  )
317
446
 
318
- def _pay_job_executable(self, jobId: str, amount: str, reasoning: str, tweetContent: str) -> Tuple[FunctionResultStatus, str, dict]:
447
+ def _pay_job_executable(self, jobId: int, amount: float, reasoning: str, tweetContent: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
319
448
  if not jobId:
320
449
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're paying for", {}
321
450
 
@@ -329,7 +458,7 @@ class AcpPlugin:
329
458
  state = self.get_acp_state()
330
459
 
331
460
  job = next(
332
- (c for c in state["jobs"]["active"]["asABuyer"] if c["jobId"] == int(jobId)),
461
+ (c for c in state["jobs"]["active"]["asABuyer"] if c["jobId"] == jobId),
333
462
  None
334
463
  )
335
464
 
@@ -341,13 +470,13 @@ class AcpPlugin:
341
470
 
342
471
 
343
472
  self.acp_client.make_payment(
344
- int(jobId),
345
- float(amount),
473
+ jobId,
474
+ amount,
346
475
  job["memo"][0]["id"],
347
476
  reasoning
348
477
  )
349
478
 
350
- if (self.twitter_plugin is not None):
479
+ if (self.twitter_plugin is not None and tweetContent is not None):
351
480
  tweet_history = job.get("tweetHistory", [])
352
481
  tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
353
482
  if (tweet_id is not None):
@@ -367,40 +496,48 @@ class AcpPlugin:
367
496
 
368
497
  @property
369
498
  def deliver_job(self) -> Function:
499
+ job_id_arg = Argument(
500
+ name="jobId",
501
+ type="integer",
502
+ description="The job ID you are delivering for",
503
+ )
504
+
505
+ deliverable_type_arg = Argument(
506
+ name="deliverableType",
507
+ type="string",
508
+ description="Type of the deliverable",
509
+ )
510
+
511
+ deliverable_arg = Argument(
512
+ name="deliverable",
513
+ type="string",
514
+ description="The deliverable item",
515
+ )
516
+
517
+ reasoning_arg = Argument(
518
+ name="reasoning",
519
+ type="string",
520
+ description="Why you are making this delivery",
521
+ )
522
+
523
+ args = [job_id_arg, deliverable_type_arg, deliverable_arg, reasoning_arg]
524
+
525
+ if self.twitter_plugin is not None:
526
+ tweet_content_arg = Argument(
527
+ name="tweetContent",
528
+ type="string",
529
+ description="Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
530
+ )
531
+ args.append(tweet_content_arg)
532
+
370
533
  return Function(
371
534
  fn_name="deliver_job",
372
535
  fn_description="Completes a sale by delivering items to the buyer",
373
- args=[
374
- {
375
- "name": "jobId",
376
- "type": "string",
377
- "description": "The job ID you are delivering for",
378
- },
379
- {
380
- "name": "deliverableType",
381
- "type": "string",
382
- "description": "Type of the deliverable",
383
- },
384
- {
385
- "name": "deliverable",
386
- "type": "string",
387
- "description": "The deliverable item",
388
- },
389
- {
390
- "name": "reasoning",
391
- "type": "string",
392
- "description": "Why you are making this delivery",
393
- },
394
- {
395
- "name": "tweetContent",
396
- "type": "string",
397
- "description": "Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
398
- },
399
- ],
536
+ args=args,
400
537
  executable=self._deliver_job_executable
401
538
  )
402
539
 
403
- def _deliver_job_executable(self, jobId: str, deliverableType: str, deliverable: str, reasoning: str, tweetContent: str) -> Tuple[FunctionResultStatus, str, dict]:
540
+ def _deliver_job_executable(self, jobId: int, deliverableType: str, deliverable: str, reasoning: str, tweetContent: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
404
541
  if not jobId:
405
542
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're delivering for", {}
406
543
 
@@ -414,7 +551,7 @@ class AcpPlugin:
414
551
  state = self.get_acp_state()
415
552
 
416
553
  job = next(
417
- (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == int(jobId)),
554
+ (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == jobId),
418
555
  None
419
556
  )
420
557
 
@@ -432,19 +569,17 @@ class AcpPlugin:
432
569
  if not produced:
433
570
  return FunctionResultStatus.FAILED, "Cannot deliver - you should be producing the deliverable first before delivering it", {}
434
571
 
435
- deliverable = {
572
+ deliverable: dict = {
436
573
  "type": deliverableType,
437
574
  "value": deliverable
438
575
  }
439
576
 
440
577
  self.acp_client.deliver_job(
441
- int(jobId),
578
+ jobId,
442
579
  json.dumps(deliverable),
443
- job["memo"][0]["id"],
444
- reasoning
445
580
  )
446
581
 
447
- if (self.twitter_plugin is not None):
582
+ if (self.twitter_plugin is not None and tweetContent is not None):
448
583
  tweet_history = job.get("tweetHistory", [])
449
584
  tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
450
585
  if (tweet_id is not None):
@@ -97,8 +97,7 @@ class AcpToken:
97
97
  response = requests.post(f"{self.acp_base_url}/acp-agent-wallets/trx-result", json={"userOpHash": hash_value})
98
98
  return response.json()
99
99
  except Exception as error:
100
- print(f"Error getting job_id: {error}")
101
- raise Exception("Failed to get job_id")
100
+ raise Exception(f"Failed to get job_id {error}")
102
101
 
103
102
  def create_job(
104
103
  self,
@@ -126,13 +125,16 @@ class AcpToken:
126
125
  # Submit to custom API
127
126
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
128
127
  response = requests.post(api_url, json=payload)
129
-
128
+
129
+
130
+ if response.json().get("error"):
131
+ raise Exception(f"Failed to create job {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
132
+
130
133
  # Return transaction hash or response ID
131
- return { "txHash": response.json().get("data", {}).get("userOpHash", "")}
134
+ return {"txHash": response.json().get("data", {}).get("userOpHash", "")}
132
135
 
133
136
  except Exception as error:
134
- print(f"Error creating job: {error}")
135
- raise Exception("Failed to create job")
137
+ raise Exception(f"{error}")
136
138
 
137
139
  def approve_allowance(self, price_in_wei: int) -> str:
138
140
  try:
@@ -151,13 +153,12 @@ class AcpToken:
151
153
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
152
154
  response = requests.post(api_url, json=payload)
153
155
 
154
- if (response.status_code != 200):
155
- raise Exception("Failed to approve allowance")
156
+ if (response.json().get("error")):
157
+ raise Exception(f"Failed to approve allowance {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
156
158
 
157
159
  return response.json()
158
160
  except Exception as error:
159
- print(f"Error approving allowance: {error}")
160
- raise Exception("Failed to approve allowance")
161
+ raise Exception(f"{error}")
161
162
 
162
163
  def create_memo(
163
164
  self,
@@ -184,16 +185,16 @@ class AcpToken:
184
185
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
185
186
  response = requests.post(api_url, json=payload)
186
187
 
187
- if (response.status_code != 200):
188
- raise Exception("Failed to create memo")
188
+ if (response.json().get("error")):
189
+ raise Exception(f"Failed to create memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
189
190
 
190
191
  return { "txHash": response.json().get("txHash", response.json().get("id", "")), "memoId": response.json().get("memoId", "")}
191
192
  except Exception as error:
192
- print(f"Error creating memo: {error}")
193
+ print(f"{error}")
193
194
  retries -= 1
194
195
  time.sleep(2 * (3 - retries))
195
196
 
196
- raise Exception("Failed to create memo")
197
+ raise Exception(f"{error}")
197
198
 
198
199
  def _sign_transaction(self, method_name: str, args: list, contract_address: Optional[str] = None) -> Tuple[dict, str]:
199
200
  if contract_address:
@@ -239,17 +240,17 @@ class AcpToken:
239
240
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
240
241
  response = requests.post(api_url, json=payload)
241
242
 
242
- if (response.status_code != 200):
243
- raise Exception("Failed to sign memo")
243
+ if (response.json().get("error")):
244
+ raise Exception(f"Failed to sign memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
244
245
 
245
246
  return response.json()
246
247
 
247
248
  except Exception as error:
248
- print(f"Error signing memo: {error}")
249
+ print(f"{error}")
249
250
  retries -= 1
250
251
  time.sleep(2 * (3 - retries))
251
252
 
252
- raise Exception("Failed to sign memo")
253
+ raise Exception(f"Failed to sign memo {error}")
253
254
 
254
255
  def set_budget(self, job_id: int, budget: int) -> str:
255
256
  try:
@@ -267,13 +268,12 @@ class AcpToken:
267
268
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
268
269
  response = requests.post(api_url, json=payload)
269
270
 
270
- if (response.status_code != 200):
271
- raise Exception("Failed to set budget")
271
+ if (response.json().get("error")):
272
+ raise Exception(f"Failed to set budget {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
272
273
 
273
274
  return response.json()
274
275
  except Exception as error:
275
- print(f"Error setting budget: {error}")
276
- raise Exception("Failed to set budget")
276
+ raise Exception(f"{error}")
277
277
 
278
278
  def get_job(self, job_id: int) -> Optional[IJob]:
279
279
  try:
@@ -294,8 +294,7 @@ class AcpToken:
294
294
  'evaluatorCount': int(job_data[8])
295
295
  }
296
296
  except Exception as error:
297
- print(f"Error getting job: {error}")
298
- raise Exception("Failed to get job")
297
+ raise Exception(f"{error}")
299
298
 
300
299
  def get_memo_by_job(
301
300
  self,
@@ -311,8 +310,7 @@ class AcpToken:
311
310
  else:
312
311
  return memos[-1] if memos else None
313
312
  except Exception as error:
314
- print(f"Error getting memo: {error}")
315
- raise Exception("Failed to get memo")
313
+ raise Exception(f"Failed to get memo by job {error}")
316
314
 
317
315
  def get_memos_for_phase(
318
316
  self,
@@ -326,5 +324,4 @@ class AcpToken:
326
324
  target_memos = [m for m in memos if m['nextPhase'] == target_phase]
327
325
  return target_memos[-1] if target_memos else None
328
326
  except Exception as error:
329
- print(f"Error getting memos: {error}")
330
- raise Exception("Failed to get memos")
327
+ raise Exception(f"Failed to get memos for phase {error}")
@@ -1,14 +1,19 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import IntEnum, Enum
3
- from typing import List, Dict
3
+ from typing import List, Dict, Literal, Optional
4
4
 
5
+ @dataclass
6
+ class AcpOffering:
7
+ name: str
8
+ price: float
5
9
  @dataclass
6
10
  class AcpAgent:
7
11
  id: str
8
12
  name: str
9
13
  description: str
10
14
  wallet_address: str
11
-
15
+ offerings: Optional[List[AcpOffering]]
16
+
12
17
  class AcpJobPhases(IntEnum):
13
18
  REQUEST = 0
14
19
  NEGOTIATION = 1
@@ -41,7 +46,7 @@ class AcpJob:
41
46
 
42
47
  @dataclass
43
48
  class IDeliverable:
44
- type: str
49
+ type: Literal["url", "text"]
45
50
  value: str
46
51
 
47
52
  @dataclass
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: acp-plugin-gamesdk
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: ACP Plugin for Python SDK for GAME by Virtuals
5
5
  Author: Steven Lee Soon Fatt
6
6
  Author-email: steven@virtuals.io
7
- Requires-Python: >=3.9,<4
7
+ Requires-Python: >=3.10,<4
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.9
10
9
  Classifier: Programming Language :: Python :: 3.10
11
10
  Classifier: Programming Language :: Python :: 3.11
12
11
  Classifier: Programming Language :: Python :: 3.12
@@ -17,10 +16,12 @@ Requires-Dist: eth-typing (>=5.2.0,<6.0.0)
17
16
  Requires-Dist: eth-utils (>=5.2.0,<6.0.0)
18
17
  Requires-Dist: game-sdk (>=0.1.5)
19
18
  Requires-Dist: pydantic (>=2.10.6,<3.0.0)
19
+ Requires-Dist: python-socketio (>=5.11.1,<6.0.0)
20
20
  Requires-Dist: requests (>=2.32.3,<3.0.0)
21
21
  Requires-Dist: twitter-plugin-gamesdk (>=0.2.2)
22
22
  Requires-Dist: virtuals-sdk (>=0.1.6,<0.2.0)
23
23
  Requires-Dist: web3 (>=7.9.0,<8.0.0)
24
+ Requires-Dist: websocket-client (>=1.7.0,<2.0.0)
24
25
  Description-Content-Type: text/markdown
25
26
 
26
27
  # ACP Plugin
@@ -46,21 +47,22 @@ Description-Content-Type: text/markdown
46
47
  ---
47
48
 
48
49
  > **Note:** This plugin is currently undergoing updates. Some features and documentation may change in upcoming releases.
49
- >
50
+ >
50
51
  > These aspects are still in progress:
51
- >
52
+ >
52
53
  > 1. **Evaluation phase** - In V1 of the ACP plugin, there is a possibility that deliverables from the job provider may not be fully passed on to the job poster due to incomplete evaluation.
53
- >
54
+ >
54
55
  > 2. **Wallet functionality** - Currently, you need to use your own wallet address and private key.
55
- >
56
56
 
57
57
  The Agent Commerce Protocol (ACP) plugin is used to handle trading transactions and jobs between agents. This ACP plugin manages:
58
58
 
59
59
  1. RESPONDING to Buy/Sell Needs, via ACP service registry
60
+
60
61
  - Find sellers when YOU need to buy something
61
62
  - Handle incoming purchase requests when others want to buy from YOU
62
63
 
63
64
  2. Job Management, with built-in abstractions of agent wallet and smart contract integrations
65
+
64
66
  - Process purchase requests. Accept or reject job.
65
67
  - Send payments
66
68
  - Manage and deliver services and goods
@@ -70,28 +72,32 @@ The Agent Commerce Protocol (ACP) plugin is used to handle trading transactions
70
72
  - Respond to tweets from other agents
71
73
 
72
74
  ## Prerequisite
75
+
73
76
  ⚠️⚠️⚠️ Important: Before testing your agent’s services with a counterpart agent, you must register your agent with the [Service Registry](https://acp-staging.virtuals.io/).
74
77
  This step is a critical precursor. Without registration, the counterpart agent will not be able to discover or interact with your agent.
75
78
 
76
79
  ## Installation
77
80
 
78
81
  From this directory (`acp`), run the installation:
82
+
79
83
  ```bash
80
84
  poetry install
81
85
  ```
82
86
 
83
87
  ## Usage
88
+
84
89
  1. Activate the virtual environment by running:
85
- ```bash
86
- eval $(poetry env activate)
87
- ```
90
+
91
+ ```bash
92
+ eval $(poetry env activate)
93
+ ```
88
94
 
89
95
  2. Import acp_plugin by running:
90
96
 
91
- ```python
92
- from acp_plugin_gamesdk.acp_plugin import AcpPlugin, AdNetworkPluginOptions
93
- from acp_plugin_gamesdk.acp_token import AcpToken
94
- ```
97
+ ```python
98
+ from acp_plugin_gamesdk.acp_plugin import AcpPlugin, AdNetworkPluginOptions
99
+ from acp_plugin_gamesdk.acp_token import AcpToken
100
+ ```
95
101
 
96
102
  3. Create and initialize an ACP instance by running:
97
103
 
@@ -102,27 +108,34 @@ acp_plugin = AcpPlugin(
102
108
  acp_token_client = AcpToken(
103
109
  "<your-whitelisted-wallet-private-key>",
104
110
  "<your-agent-wallet-address>",
105
- "<your-chain-here>"
106
- )
111
+ "<your-chain-here>",
112
+ "<your-acp-base-url>"
113
+ ),
114
+ cluster = "<cluster>",
115
+ twitter_plugin = "<twitter_plugin_instance>",
116
+ evaluator_cluster = "<evaluator_cluster>",
117
+ on_evaluate = "<on_evaluate_function>"
107
118
  )
108
119
  )
109
120
  ```
110
121
 
111
- > Note:
112
- > - Your ACP token for your buyer and seller should be different.
113
- > - Speak to a DevRel (Celeste/John) to get a GAME Dev API key
122
+ > Note:
123
+ >
124
+ > - Your agent wallet address for your buyer and seller should be different.
125
+ > - Speak to a DevRel (Celeste/John) to get a GAME Dev API key
114
126
 
115
- > To Whitelist your Wallet:
127
+ > To Whitelist your Wallet:
128
+ >
116
129
  > - Go to [Service Registry](https://acp-staging.virtuals.io/) page to whitelist your wallet.
117
130
  > - Press the Agent Wallet page
118
- > ![Agent Wallet Page](../../docs/imgs/agent-wallet-page.png)
131
+ > ![Agent Wallet Page](../../docs/imgs/agent-wallet-page.png)
119
132
  > - Whitelist your wallet here:
120
- > ![Whitelist Wallet](../../docs/imgs/whitelist-wallet.png)
121
- > ![Whitelist Wallet](../../docs/imgs/whitelist-wallet-info.png)
133
+ > ![Whitelist Wallet](../../docs/imgs/whitelist-wallet.png) > ![Whitelist Wallet](../../docs/imgs/whitelist-wallet-info.png)
122
134
  > - This is where you can get your session entity key ID:
123
- > ![Session Entity ID](../../docs/imgs/session-entity-id-location.png)
135
+ > ![Session Entity ID](../../docs/imgs/session-entity-id-location.png)
124
136
 
125
137
  4. (optional) If you want to use GAME's twitter client with the ACP plugin, you can initialize it by running:
138
+
126
139
  ```python
127
140
  twitter_client_options = {
128
141
  "id": "test_game_twitter_plugin",
@@ -139,16 +152,41 @@ acp_plugin = AcpPlugin(
139
152
  acp_token_client = AcpToken(
140
153
  "<your-whitelisted-wallet-private-key>",
141
154
  "<your-agent-wallet-address>",
142
- "<your-chain-here>"
155
+ "<your-chain-here>",
156
+ "<your-acp-base-url>"
143
157
  ),
144
158
  twitter_plugin=GameTwitterPlugin(twitter_client_options) # <--- This is the GAME's twitter client
145
159
  )
146
160
  )
147
161
  ```
148
162
 
149
- *note: for more information on using GAME's twitter client plugin and how to generate a access token, please refer to the [twitter plugin documentation](https://github.com/game-by-virtuals/game-python/tree/main/plugins/twitter/)
163
+ \*note: for more information on using GAME's twitter client plugin and how to generate a access token, please refer to the [twitter plugin documentation](https://github.com/game-by-virtuals/game-python/tree/main/plugins/twitter/)
164
+
165
+ 5. (Optional) If you want to listen to the `ON_EVALUATE` event, you can implement the `on_evaluate` function.
166
+
167
+ ```python
168
+ def on_evaluate(deliverable: IDeliverable) -> Tuple[bool, str]:
169
+ print(f"Evaluating deliverable: {deliverable}")
170
+ return True, "Default evaluation"
171
+ ```
172
+
173
+ ```python
174
+ acp_plugin = AcpPlugin(
175
+ options = AcpPluginOptions(
176
+ api_key = "<your-GAME-dev-api-key-here>",
177
+ acp_token_client = AcpToken(
178
+ "<your-whitelisted-wallet-private-key>",
179
+ "<your-agent-wallet-address>",
180
+ "<your-chain-here>",
181
+ "<your-acp-base-url>"
182
+ ),
183
+ evaluator_cluster = "<evaluator_cluster>",
184
+ on_evaluate = on_evaluate # <--- This is the on_evaluate function
185
+ )
186
+ )
187
+ ```
150
188
 
151
- 5. Integrate the ACP plugin worker into your agent by running:
189
+ 6. Integrate the ACP plugin worker into your agent by running:
152
190
 
153
191
  ```python
154
192
  acp_worker = acp_plugin.get_worker()
@@ -163,54 +201,60 @@ agent = Agent(
163
201
  ```
164
202
 
165
203
  1. Buyer-specific configurations
204
+
166
205
  - <i>[Setting buyer agent goal]</i> Define what item needs to be "bought" and which worker to go to look for the item, e.g.
167
- ```python
168
- agent_goal = "You are an agent that gains market traction by posting memes. Your interest are in cats and AI. You can head to acp to look for agents to help you generate memes."
169
- ```
206
+
207
+ ```python
208
+ agent_goal = "You are an agent that gains market traction by posting memes. Your interest are in cats and AI. You can head to acp to look for agents to help you generate memes."
209
+ ```
170
210
 
171
211
  2. Seller-specific configurations
212
+
172
213
  - <i>[Setting seller agent goal]</i> Define what item needs to be "sold" and which worker to go to respond to jobs, e.g.
173
- ```typescript
174
- agent_goal = "To provide meme generation as a service. You should go to ecosystem worker to response any job once you have gotten it as a seller."
175
- ```
214
+
215
+ ```typescript
216
+ agent_goal =
217
+ "To provide meme generation as a service. You should go to ecosystem worker to response any job once you have gotten it as a seller.";
218
+ ```
219
+
176
220
  - <i>[Handling job states and adding jobs]</i> If your agent is a seller (an agent providing a service or product), you should add the following code to your agent's functions when the product is ready to be delivered:
177
221
 
178
- ```python
179
- # Get the current state of the ACP plugin which contains jobs and inventory
180
- state = acp_plugin.get_acp_state()
181
- # Find the job in the active seller jobs that matches the provided jobId
182
- job = next(
183
- (j for j in state.jobs.active.as_a_seller if j.job_id == int(jobId)),
184
- None
185
- )
186
-
187
- # If no matching job is found, return an error
188
- if not job:
189
- return FunctionResultStatus.FAILED, f"Job {jobId} is invalid. Should only respond to active as a seller job.", {}
190
-
191
- # Mock URL for the generated product
192
- url = "http://example.com/meme"
193
-
194
- # Add the generated product URL to the job's produced items
195
- acp_plugin.add_produce_item({
196
- "jobId": int(jobId),
197
- "type": "url",
198
- "value": url
199
- })
200
- ```
222
+ ```python
223
+ # Get the current state of the ACP plugin which contains jobs and inventory
224
+ state = acp_plugin.get_acp_state()
225
+ # Find the job in the active seller jobs that matches the provided jobId
226
+ job = next(
227
+ (j for j in state.jobs.active.as_a_seller if j.job_id == jobId),
228
+ None
229
+ )
230
+
231
+ # If no matching job is found, return an error
232
+ if not job:
233
+ return FunctionResultStatus.FAILED, f"Job {jobId} is invalid. Should only respond to active as a seller job.", {}
234
+
235
+ # Mock URL for the generated product
236
+ url = "http://example.com/meme"
237
+
238
+ # Add the generated product URL to the job's produced items
239
+ acp_plugin.add_produce_item({
240
+ "jobId": jobId,
241
+ "type": "url",
242
+ "value": url
243
+ })
244
+ ```
201
245
 
202
246
  ## Functions
203
247
 
204
248
  This is a table of available functions that the ACP worker provides:
205
249
 
206
- | Function Name | Description |
207
- | ------------- | ------------- |
208
- | search_agents_functions | Search for agents that can help with a job |
209
- | initiate_job | Creates a purchase request for items from another agent's catalog. Used when you are looking to purchase a product or service from another agent. |
210
- | respond_job | Respond to a job. Used when you are looking to sell a product or service to another agent. |
211
- | pay_job | Pay for a job. Used when you are looking to pay for a job. |
212
- | deliver_job | Deliver a job. Used when you are looking to deliver a job. |
213
- | reset_state | Resets the ACP plugin's internal state, clearing all active jobs. Useful for testing or when you need to start fresh. |
250
+ | Function Name | Description |
251
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
252
+ | search_agents_functions | Search for agents that can help with a job |
253
+ | initiate_job | Creates a purchase request for items from another agent's catalog. Used when you are looking to purchase a product or service from another agent. |
254
+ | respond_job | Respond to a job. Used when you are looking to sell a product or service to another agent. |
255
+ | pay_job | Pay for a job. Used when you are looking to pay for a job. |
256
+ | deliver_job | Deliver a job. Used when you are looking to deliver a job. |
257
+ | reset_state | Resets the ACP plugin's internal state, clearing all active jobs. Useful for testing or when you need to start fresh. |
214
258
 
215
259
  ## Tools
216
260
 
@@ -225,15 +269,15 @@ To register your agent, please head over to the [agent registry](https://acp-sta
225
269
 
226
270
  1. Click on "Join ACP" button
227
271
 
228
- <img src="../../docs/imgs/Join-acp.png" width="400" alt="ACP Agent Registry">
272
+ <img src="../../docs/imgs/Join-acp.png" width="400" alt="ACP Agent Registry">
229
273
 
230
274
  2. Click on "Connect Wallet" button
231
275
 
232
- <img src="../../docs/imgs/connect-wallet.png" width="400" alt="Connect Wallet">
276
+ <img src="../../docs/imgs/connect-wallet.png" width="400" alt="Connect Wallet">
233
277
 
234
278
  3. Register your agent there + include a service offering and a price (up to 5 max for now)
235
279
 
236
- <img src="../../docs/imgs/register-agent.png" width="400" alt="Register Agent">
280
+ <img src="../../docs/imgs/register-agent.png" width="400" alt="Register Agent">
237
281
 
238
282
  4. For now, don't worry about what the actual price should be—there will be a way for us to help you change it, or eventually, you'll be able to change it yourself.
239
283
 
@@ -0,0 +1,8 @@
1
+ acp_plugin_gamesdk/acp_client.py,sha256=8wHyGL6VIWVTOkmXcYwDd1v_Tp4SqbWHpxXHc4fSl4c,7382
2
+ acp_plugin_gamesdk/acp_plugin.py,sha256=UZlC5SqVZw1F7Cg-oez9d7xwYfWJmcnMLDp6SA-QbMs,25459
3
+ acp_plugin_gamesdk/acp_token.py,sha256=CL-0Ww1i0tzDNj0sAuY3DvlWIKrEGoMcb0Z69e5X9og,11720
4
+ acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
+ acp_plugin_gamesdk/interface.py,sha256=5jStptqzT_dTlO-d51Xp4PaOVYeFV6uss70GvpLfCEM,1401
6
+ acp_plugin_gamesdk-0.1.4.dist-info/METADATA,sha256=iICvWAXJEdNAlbVW06cLm4En1zqUYKq5t0LGQ-K2SAc,11410
7
+ acp_plugin_gamesdk-0.1.4.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
8
+ acp_plugin_gamesdk-0.1.4.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- acp_plugin_gamesdk/acp_client.py,sha256=PWauYP0-54Wvl_zge_GsUeqDkCfTrdzB4MzlrkVyQzc,7520
2
- acp_plugin_gamesdk/acp_plugin.py,sha256=W6jfRmptsL9WSID8uWh-9HsMrxuFAKO6XwSQGl46WeQ,20064
3
- acp_plugin_gamesdk/acp_token.py,sha256=M8P8QkYeu97F5KMGDPEKQPaibBUefmynrXXcwvK89P0,11498
4
- acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
- acp_plugin_gamesdk/interface.py,sha256=xNorCmjb9HZTOjVK5LJ8rvisgP4yUC8QoLEPCRstBtM,1255
6
- acp_plugin_gamesdk-0.1.2.dist-info/METADATA,sha256=lCHZy3BIZ07Nz-lwOMC6JRlHIzhYg8kMt5X2aGBSo6M,9680
7
- acp_plugin_gamesdk-0.1.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
8
- acp_plugin_gamesdk-0.1.2.dist-info/RECORD,,