acp-plugin-gamesdk 0.1.19__py3-none-any.whl → 0.1.21__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.
@@ -1,15 +1,20 @@
1
- import json
2
- import requests
3
1
  import time
4
2
  import traceback
5
-
6
- from datetime import datetime, timedelta, timezone
3
+ from datetime import datetime, timezone
7
4
  from typing import List, Optional
5
+
6
+ import requests
7
+ from dacite import Config, from_dict
8
8
  from web3 import Web3
9
9
 
10
- from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpOffering, AcpState, AcpJobPhasesDesc
11
10
  from acp_plugin_gamesdk.acp_token import AcpToken, MemoType
12
- from dacite import from_dict, Config
11
+ from acp_plugin_gamesdk.interface import (
12
+ AcpAgent,
13
+ AcpJobPhases,
14
+ AcpJobPhasesDesc,
15
+ AcpOffering,
16
+ AcpState,
17
+ )
13
18
 
14
19
 
15
20
  class AcpClient:
@@ -26,22 +31,23 @@ class AcpClient:
26
31
  return self.acp_token.get_agent_wallet_address()
27
32
 
28
33
  def get_state(self) -> AcpState:
29
- response = requests.get(
34
+ response = requests.get(
30
35
  f"{self.base_url}/states/{self.agent_wallet_address}",
31
36
  headers={"x-api-key": self.api_key}
32
37
  )
33
38
  payload = response.json()
34
- result = from_dict(data_class=AcpState, data=payload, config=Config(type_hooks={AcpJobPhasesDesc: AcpJobPhasesDesc}))
39
+ result = from_dict(data_class=AcpState, data=payload,
40
+ config=Config(type_hooks={AcpJobPhasesDesc: AcpJobPhasesDesc}))
35
41
  return result
36
42
 
37
43
  def browse_agents(
38
- self,
39
- cluster: Optional[str] = None,
40
- query: Optional[str] = None,
41
- rerank: Optional[bool] = True,
42
- top_k: Optional[int] = 1,
44
+ self,
45
+ cluster: Optional[str] = None,
46
+ query: Optional[str] = None,
47
+ rerank: Optional[bool] = True,
48
+ top_k: Optional[int] = 1,
43
49
  ) -> List[AcpAgent]:
44
-
50
+
45
51
  url = f"{self.acp_base_url}/agents"
46
52
 
47
53
  params = {
@@ -55,22 +61,22 @@ class AcpClient:
55
61
 
56
62
  if response.status_code != 200:
57
63
  raise Exception(
58
- f"Error occured in browse_agents function. Failed to browse agents.\n"
59
- f"Response status code: {response.status_code}\n"
60
- f"Response description: {response.text}\n"
61
- )
62
-
64
+ f"Error occured in browse_agents function. Failed to browse agents.\n"
65
+ f"Response status code: {response.status_code}\n"
66
+ f"Response description: {response.text}\n"
67
+ )
63
68
 
64
69
  response_json = response.json()
65
-
70
+
66
71
  result = []
67
-
72
+
68
73
  for agent in response_json.get("data", []):
69
74
  if agent["offerings"]:
70
- offerings = [AcpOffering(name=offering["name"], price=offering["price"]) for offering in agent["offerings"]]
75
+ offerings = [AcpOffering(name=offering["name"], price=offering["price"]) for offering in
76
+ agent["offerings"]]
71
77
  else:
72
78
  offerings = None
73
-
79
+
74
80
  result.append(
75
81
  AcpAgent(
76
82
  id=agent["id"],
@@ -83,7 +89,7 @@ class AcpClient:
83
89
  explanation=agent["explanation"]
84
90
  )
85
91
  )
86
-
92
+
87
93
  return result
88
94
 
89
95
  def create_job(
@@ -95,53 +101,53 @@ class AcpClient:
95
101
  expired_at: datetime,
96
102
  ) -> int:
97
103
  tx_result = self.acp_token.create_job(
98
- provider_address = provider_address,
99
- evaluator_address = evaluator_address,
100
- expire_at = expired_at
104
+ provider_address=provider_address,
105
+ evaluator_address=evaluator_address,
106
+ expire_at=expired_at
101
107
  )
102
-
108
+
103
109
  job_id = None
104
110
  retry_count = 3
105
111
  retry_delay = 3
106
-
107
- time.sleep(retry_delay)
112
+
113
+ time.sleep(retry_delay)
108
114
  for attempt in range(retry_count):
109
115
  try:
110
116
  response = self.acp_token.validate_transaction(tx_result["txHash"])
111
117
  data = response.get("data", {})
112
118
  if not data:
113
119
  raise Exception("Invalid tx_hash!")
114
-
120
+
115
121
  if data.get("status") == "retry":
116
122
  raise Exception("Transaction failed, retrying...")
117
-
123
+
118
124
  if data.get("status") == "failed":
119
125
  break
120
-
126
+
121
127
  if data.get("status") == "success":
122
128
  job_id = int(data.get("result").get("jobId"))
123
-
129
+
124
130
  if job_id is not None and job_id != "":
125
- break
126
-
131
+ break
132
+
127
133
  except Exception as e:
128
134
  print(f"Error in create_job function: {e}")
129
135
  print(traceback.format_exc())
130
136
  if attempt < retry_count - 1:
131
- time.sleep(retry_delay)
137
+ time.sleep(retry_delay)
132
138
  else:
133
139
  raise
134
-
140
+
135
141
  if job_id is None or job_id == "":
136
142
  raise Exception("Failed to create job")
137
-
143
+
138
144
  self.acp_token.create_memo(
139
- job_id=job_id,
140
- content=job_description,
141
- memo_type=MemoType.MESSAGE,
142
- is_secured=False,
143
- next_phase=AcpJobPhases.NEGOTIATION
144
- )
145
+ job_id=job_id,
146
+ content=job_description,
147
+ memo_type=MemoType.MESSAGE,
148
+ is_secured=False,
149
+ next_phase=AcpJobPhases.NEGOTIATION
150
+ )
145
151
 
146
152
  payload = {
147
153
  "jobId": job_id,
@@ -169,7 +175,7 @@ class AcpClient:
169
175
  if accept:
170
176
  self.acp_token.sign_memo(memo_id, accept, reasoning)
171
177
  time.sleep(5)
172
-
178
+
173
179
  return self.acp_token.create_memo(
174
180
  job_id=job_id,
175
181
  content=f"Job {job_id} accepted. {reasoning}",
@@ -189,7 +195,7 @@ class AcpClient:
189
195
  def make_payment(self, job_id: int, amount: float, memo_id: int, reason: str):
190
196
  # Convert amount to Wei (smallest ETH unit)
191
197
  amount_wei = self.web3.to_wei(amount, 'ether')
192
-
198
+
193
199
  self.acp_token.set_budget(job_id, amount_wei)
194
200
  time.sleep(5)
195
201
  self.acp_token.approve_allowance(amount_wei)
@@ -218,7 +224,7 @@ class AcpClient:
218
224
  "tweetId": tweet_id,
219
225
  "content": content
220
226
  }
221
-
227
+
222
228
  response = requests.post(
223
229
  f"{self.base_url}/{job_id}/tweets/{self.agent_wallet_address}",
224
230
  json=payload,
@@ -228,65 +234,65 @@ class AcpClient:
228
234
  "x-api-key": self.api_key
229
235
  }
230
236
  )
231
-
237
+
232
238
  if response.status_code != 200 and response.status_code != 201:
233
239
  raise Exception(
234
- f"Error occured in add_tweet function. Failed to add tweet.\n"
240
+ f"Error occured in add_tweet function. Failed to add tweet.\n"
235
241
  f"Response status code: {response.status_code}\n"
236
242
  f"Response description: {response.text}\n"
237
243
  )
238
-
239
-
244
+
240
245
  return response.json()
241
-
246
+
242
247
  def reset_state(self) -> None:
243
248
  response = requests.delete(
244
249
  f"{self.base_url}/states/{self.agent_wallet_address}",
245
250
  headers={"x-api-key": self.api_key}
246
251
  )
247
-
252
+
248
253
  if response.status_code not in [200, 204]:
249
254
  raise Exception(
250
- f"Error occured in reset_state function. Failed to reset state\n"
255
+ f"Error occured in reset_state function. Failed to reset state\n"
251
256
  f"Response status code: {response.status_code}\n"
252
257
  f"Response description: {response.text}\n"
253
258
  )
254
-
259
+
255
260
  def delete_completed_job(self, job_id: int) -> None:
256
261
  response = requests.delete(
257
262
  f"{self.base_url}/{job_id}/wallet/{self.agent_wallet_address}",
258
263
  headers={"x-api-key": self.api_key}
259
264
  )
260
-
265
+
261
266
  if response.status_code not in [200, 204]:
262
267
  raise Exception(
263
268
  f"Error occurred in delete_completed_job function. Failed to delete job.\n"
264
269
  f"Response status code: {response.status_code}\n"
265
270
  f"Response description: {response.text}\n"
266
271
  )
267
-
272
+
268
273
  def get_agent_by_wallet_address(self, wallet_address: str) -> AcpAgent:
269
274
  url = f"{self.acp_base_url}/agents?filters[walletAddress]={wallet_address}"
270
-
275
+
271
276
  response = requests.get(
272
277
  url,
273
278
  )
274
-
279
+
275
280
  if response.status_code != 200:
276
281
  raise Exception(
277
282
  f"Failed to get agent: {response.status_code} {response.text}"
278
283
  )
279
-
284
+
280
285
  response_json = response.json()
281
-
286
+
282
287
  result = []
283
-
288
+
284
289
  for agent in response_json.get("data", []):
285
290
  if agent["offerings"]:
286
- offerings = [AcpOffering(name=offering["name"], price=offering["price"]) for offering in agent["offerings"]]
291
+ offerings = [AcpOffering(name=offering["name"], price=offering["price"]) for offering in
292
+ agent["offerings"]]
287
293
  else:
288
294
  offerings = None
289
-
295
+
290
296
  result.append(
291
297
  AcpAgent(
292
298
  id=agent["id"],
@@ -1,22 +1,27 @@
1
- from collections.abc import Callable
1
+ import json
2
2
  import signal
3
3
  import sys
4
- from typing import List, Dict, Any, Optional,Tuple
5
- import json
6
- from dataclasses import dataclass, asdict
7
- from datetime import datetime, timezone, timedelta
8
4
  import traceback
5
+ from collections.abc import Callable
6
+ from dataclasses import asdict, dataclass
7
+ from datetime import datetime, timedelta, timezone
8
+ from typing import Any, Dict, List, Optional, Tuple
9
9
 
10
10
  import socketio
11
- import socketio.client
12
11
 
13
12
  from game_sdk.game.agent import WorkerConfig
14
13
  from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus
15
- from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
16
14
  from twitter_plugin_gamesdk.game_twitter_plugin import GameTwitterPlugin
15
+ from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
17
16
  from acp_plugin_gamesdk.acp_client import AcpClient
18
17
  from acp_plugin_gamesdk.acp_token import AcpToken
19
- from acp_plugin_gamesdk.interface import AcpJobPhasesDesc, IDeliverable, IInventory, AcpJob
18
+ from acp_plugin_gamesdk.interface import (
19
+ AcpJob,
20
+ AcpJobPhasesDesc,
21
+ IDeliverable,
22
+ IInventory,
23
+ )
24
+
20
25
 
21
26
  @dataclass
22
27
  class AcpPluginOptions:
@@ -28,16 +33,17 @@ class AcpPluginOptions:
28
33
  on_evaluate: Optional[Callable[[IDeliverable], Tuple[bool, str]]] = None
29
34
  on_phase_change: Optional[Callable[[AcpJob], None]] = None
30
35
  job_expiry_duration_mins: Optional[int] = None
31
-
36
+
32
37
 
33
38
  SocketEvents = {
34
39
  "JOIN_EVALUATOR_ROOM": "joinEvaluatorRoom",
35
- "LEAVE_EVALUATOR_ROOM": "leaveEvaluatorRoom",
40
+ "LEAVE_EVALUATOR_ROOM": "leaveEvaluatorRoom",
36
41
  "ON_EVALUATE": "onEvaluate",
37
- "ROOM_JOINED" : "roomJoined",
42
+ "ROOM_JOINED": "roomJoined",
38
43
  "ON_PHASE_CHANGE": "onPhaseChange"
39
44
  }
40
45
 
46
+
41
47
  class AcpPlugin:
42
48
  def __init__(self, options: AcpPluginOptions):
43
49
  print("Initializing AcpPlugin")
@@ -65,7 +71,7 @@ class AcpPlugin:
65
71
  self.twitter_plugin = None
66
72
  if options.twitter_plugin is not None:
67
73
  self.twitter_plugin = options.twitter_plugin
68
-
74
+
69
75
  self.produced_inventory: List[IInventory] = []
70
76
  self.acp_base_url = self.acp_token_client.acp_base_url
71
77
  if options.on_evaluate is not None or options.on_phase_change is not None:
@@ -74,15 +80,14 @@ class AcpPlugin:
74
80
  if options.on_evaluate is not None:
75
81
  self.on_evaluate = options.on_evaluate
76
82
  if options.on_phase_change is not None:
77
- def phase_change_wrapper(job : AcpJob):
83
+ def phase_change_wrapper(job: AcpJob):
78
84
  job["getAgentByWalletAddress"] = self.acp_client.get_agent_by_wallet_address
79
85
  return options.on_phase_change(job)
86
+
80
87
  self.on_phase_change = phase_change_wrapper
81
88
  self.initialize_socket()
82
89
  self.job_expiry_duration_mins = options.job_expiry_duration_mins if options.job_expiry_duration_mins is not None else 1440
83
-
84
-
85
-
90
+
86
91
  def initialize_socket(self) -> Tuple[bool, str]:
87
92
  """
88
93
  Initialize socket connection for real-time communication.
@@ -90,36 +95,36 @@ class AcpPlugin:
90
95
  """
91
96
  try:
92
97
  self.socket = socketio.Client()
93
-
98
+
94
99
  # Set up authentication before connecting
95
100
  self.socket.auth = {
96
101
  "evaluatorAddress": self.acp_token_client.agent_wallet_address
97
102
  }
98
-
103
+
99
104
  # Connect socket to GAME SDK dev server
100
105
  self.socket.connect(self.acp_client.base_url, auth=self.socket.auth)
101
-
106
+
102
107
  if self.socket.connected:
103
108
  self.socket.emit(SocketEvents["JOIN_EVALUATOR_ROOM"], self.acp_token_client.agent_wallet_address)
104
-
105
-
109
+
106
110
  # Set up event handler for evaluation requests
107
111
  @self.socket.on(SocketEvents["ON_EVALUATE"])
108
112
  def on_evaluate(data):
109
113
  if self.on_evaluate:
110
114
  deliverable = data.get("deliverable")
111
115
  memo_id = data.get("memoId")
112
-
116
+
113
117
  is_approved, reasoning = self.on_evaluate(deliverable)
114
-
118
+
115
119
  self.acp_token_client.sign_memo(memo_id, is_approved, reasoning)
116
-
117
- # Set up event handler for phase changes
120
+
121
+ # Set up event handler for phase changes
122
+
118
123
  @self.socket.on(SocketEvents["ON_PHASE_CHANGE"])
119
124
  def on_phase_change(data):
120
125
  if hasattr(self, 'on_phase_change') and self.on_phase_change:
121
126
  self.on_phase_change(data)
122
-
127
+
123
128
  # Set up cleanup function for graceful shutdown
124
129
  def cleanup():
125
130
  if self.socket:
@@ -127,34 +132,31 @@ class AcpPlugin:
127
132
  import time
128
133
  time.sleep(1)
129
134
  self.socket.disconnect()
130
-
131
-
132
-
135
+
133
136
  def signal_handler(_sig, _frame):
134
137
  cleanup()
135
138
  sys.exit(0)
136
-
139
+
137
140
  signal.signal(signal.SIGINT, signal_handler)
138
141
  signal.signal(signal.SIGTERM, signal_handler)
139
-
142
+
140
143
  return True, "Socket initialized successfully"
141
-
144
+
142
145
  except Exception as e:
143
146
  return False, f"Failed to initialize socket: {str(e)}"
144
-
145
-
147
+
146
148
  def set_on_phase_change(self, on_phase_change: Callable[[AcpJob], None]) -> None:
147
149
  self.on_phase_change = on_phase_change
148
150
 
149
151
  def add_produce_item(self, item: IInventory) -> None:
150
152
  self.produced_inventory.append(item)
151
-
153
+
152
154
  def reset_state(self) -> None:
153
155
  self.acp_client.reset_state()
154
-
156
+
155
157
  def delete_completed_job(self, job_id: int) -> None:
156
158
  self.acp_client.delete_completed_job(job_id)
157
-
159
+
158
160
  def get_acp_state(self) -> Dict:
159
161
  server_state = self.acp_client.get_state()
160
162
  server_state.inventory.produced = self.produced_inventory
@@ -169,7 +171,7 @@ class AcpPlugin:
169
171
  self.pay_job,
170
172
  self.deliver_job,
171
173
  ]
172
-
174
+
173
175
  def get_environment(_function_result, _current_state) -> Dict[str, Any]:
174
176
  environment = data.get_environment() if hasattr(data, "get_environment") else {}
175
177
  return {
@@ -184,7 +186,7 @@ class AcpPlugin:
184
186
  get_state_fn=get_environment,
185
187
  instruction=data.get("instructions") if data else None
186
188
  )
187
-
189
+
188
190
  return worker_config
189
191
 
190
192
  @property
@@ -203,8 +205,8 @@ class AcpPlugin:
203
205
  - Each job tracks:
204
206
  * phase: request (seller should response to accept/reject to the job) → pending_payment (as a buyer to make the payment for the service) → in_progress (seller to deliver the service) → evaluation → completed/rejected
205
207
  """
206
-
207
- def _search_agents_executable(self,reasoning: str, keyword: str) -> Tuple[FunctionResultStatus, str, dict]:
208
+
209
+ def _search_agents_executable(self, reasoning: str, keyword: str) -> Tuple[FunctionResultStatus, str, dict]:
208
210
  if not reasoning:
209
211
  return FunctionResultStatus.FAILED, "Reasoning for the search must be provided. This helps track your decision-making process for future reference.", {}
210
212
 
@@ -291,21 +293,22 @@ class AcpPlugin:
291
293
  type="string",
292
294
  description="Detailed specifications for service-based items",
293
295
  )
294
-
296
+
295
297
  require_evaluation_arg = Argument(
296
298
  name="require_evaluation",
297
299
  type="boolean",
298
300
  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.",
299
301
  )
300
-
302
+
301
303
  evaluator_keyword_arg = Argument(
302
304
  name="evaluator_keyword",
303
305
  type="string",
304
306
  description="Keyword to search for a evaluator",
305
307
  )
306
308
 
307
- args = [seller_wallet_address_arg, price_arg, reasoning_arg, service_requirements_arg, require_evaluation_arg, evaluator_keyword_arg]
308
-
309
+ args = [seller_wallet_address_arg, price_arg, reasoning_arg, service_requirements_arg, require_evaluation_arg,
310
+ evaluator_keyword_arg]
311
+
309
312
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
310
313
  tweet_content_arg = Argument(
311
314
  name="tweet_content",
@@ -313,7 +316,7 @@ class AcpPlugin:
313
316
  description="Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
314
317
  )
315
318
  args.append(tweet_content_arg)
316
-
319
+
317
320
  return Function(
318
321
  fn_name="initiate_job",
319
322
  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.",
@@ -321,7 +324,9 @@ class AcpPlugin:
321
324
  executable=self._initiate_job_executable
322
325
  )
323
326
 
324
- def _initiate_job_executable(self, seller_wallet_address: str, price: str, reasoning: str, service_requirements: str, require_evaluation: str, evaluator_keyword: str, tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
327
+ def _initiate_job_executable(self, seller_wallet_address: str, price: str, reasoning: str,
328
+ service_requirements: str, require_evaluation: str, evaluator_keyword: str,
329
+ tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
325
330
  if isinstance(require_evaluation, str):
326
331
  require_evaluation = require_evaluation.lower() == 'true'
327
332
  elif isinstance(require_evaluation, bool):
@@ -331,10 +336,10 @@ class AcpPlugin:
331
336
 
332
337
  if not price:
333
338
  return FunctionResultStatus.FAILED, "Missing price - specify how much you're offering per unit", {}
334
-
339
+
335
340
  if not reasoning:
336
341
  return FunctionResultStatus.FAILED, "Missing reasoning - explain why you're making this purchase request", {}
337
-
342
+
338
343
  try:
339
344
  state = self.get_acp_state()
340
345
 
@@ -345,24 +350,25 @@ class AcpPlugin:
345
350
  )
346
351
 
347
352
  if existing_job:
348
- return FunctionResultStatus.FAILED, f"You already have an active job as a buyer with {existing_job['providerAddress']} - complete the current job before initiating a new one", {}
349
-
353
+ return FunctionResultStatus.FAILED, f"You already have an active job as a buyer with {existing_job['providerAddress']} - complete the current job before initiating a new one", {}
354
+
350
355
  if not seller_wallet_address:
351
356
  return FunctionResultStatus.FAILED, "Missing seller wallet address - specify the agent you want to buy from", {}
352
-
357
+
353
358
  if require_evaluation and not evaluator_keyword:
354
359
  return FunctionResultStatus.FAILED, "Missing validator keyword - provide a keyword to search for a validator", {}
355
-
360
+
356
361
  evaluator_address = self.acp_token_client.get_agent_wallet_address()
357
-
362
+
358
363
  if require_evaluation:
359
- validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluator_keyword, rerank=True, top_k=1)
360
-
364
+ validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluator_keyword, rerank=True,
365
+ top_k=1)
366
+
361
367
  if len(validators) == 0:
362
368
  return FunctionResultStatus.FAILED, "No evaluator found - try a different keyword", {}
363
369
 
364
370
  evaluator_address = validators[0].wallet_address
365
-
371
+
366
372
  # ... Rest of validation logic ...
367
373
  expired_at = datetime.now(timezone.utc) + timedelta(minutes=self.job_expiry_duration_mins)
368
374
  job_id = self.acp_client.create_job(
@@ -410,9 +416,9 @@ class AcpPlugin:
410
416
  type="string",
411
417
  description="Why you made this decision",
412
418
  )
413
-
419
+
414
420
  args = [job_id_arg, decision_arg, reasoning_arg]
415
-
421
+
416
422
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
417
423
  tweet_content_arg = Argument(
418
424
  name="tweet_content",
@@ -428,19 +434,20 @@ class AcpPlugin:
428
434
  executable=self._respond_job_executable
429
435
  )
430
436
 
431
- def _respond_job_executable(self, job_id: int, decision: str, reasoning: str, tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
437
+ def _respond_job_executable(self, job_id: int, decision: str, reasoning: str,
438
+ tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
432
439
  if not job_id:
433
440
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're responding to", {}
434
-
441
+
435
442
  if not decision or decision not in ["ACCEPT", "REJECT"]:
436
443
  return FunctionResultStatus.FAILED, "Invalid decision - must be either 'ACCEPT' or 'REJECT'", {}
437
-
444
+
438
445
  if not reasoning:
439
446
  return FunctionResultStatus.FAILED, "Missing reasoning - explain why you made this decision", {}
440
447
 
441
448
  try:
442
449
  state = self.get_acp_state()
443
-
450
+
444
451
  job = next(
445
452
  (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == job_id),
446
453
  None
@@ -490,7 +497,7 @@ class AcpPlugin:
490
497
  )
491
498
 
492
499
  args = [job_id_arg, amount_arg, reasoning_arg]
493
-
500
+
494
501
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
495
502
  tweet_content_arg = Argument(
496
503
  name="tweet_content",
@@ -506,7 +513,8 @@ class AcpPlugin:
506
513
  executable=self._pay_job_executable
507
514
  )
508
515
 
509
- def _pay_job_executable(self, job_id: int, amount: float, reasoning: str, tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
516
+ def _pay_job_executable(self, job_id: int, amount: float, reasoning: str, tweet_content: Optional[str] = None) -> \
517
+ Tuple[FunctionResultStatus, str, dict]:
510
518
  if not job_id:
511
519
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're paying for", {}
512
520
 
@@ -518,7 +526,7 @@ class AcpPlugin:
518
526
 
519
527
  try:
520
528
  state = self.get_acp_state()
521
-
529
+
522
530
  job = next(
523
531
  (c for c in state["jobs"]["active"]["asABuyer"] if c["jobId"] == job_id),
524
532
  None
@@ -530,7 +538,6 @@ class AcpPlugin:
530
538
  if job["phase"] != AcpJobPhasesDesc.NEGOTIATION:
531
539
  return FunctionResultStatus.FAILED, f"Cannot pay - job is in '{job['phase']}' phase, must be in 'negotiation' phase", {}
532
540
 
533
-
534
541
  self.acp_client.make_payment(
535
542
  job_id,
536
543
  amount,
@@ -570,7 +577,7 @@ class AcpPlugin:
570
577
  )
571
578
 
572
579
  args = [job_id_arg, deliverable_arg, reasoning_arg]
573
-
580
+
574
581
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
575
582
  tweet_content_arg = Argument(
576
583
  name="tweet_content",
@@ -586,19 +593,20 @@ class AcpPlugin:
586
593
  executable=self._deliver_job_executable
587
594
  )
588
595
 
589
- def _deliver_job_executable(self, job_id: int, deliverable: str, reasoning: str, tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
596
+ def _deliver_job_executable(self, job_id: int, deliverable: str, reasoning: str,
597
+ tweet_content: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
590
598
  if not job_id:
591
599
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're delivering for", {}
592
-
600
+
593
601
  if not reasoning:
594
602
  return FunctionResultStatus.FAILED, "Missing reasoning - explain why you're making this delivery", {}
595
-
603
+
596
604
  if not deliverable:
597
605
  return FunctionResultStatus.FAILED, "Missing deliverable - specify what you're delivering", {}
598
606
 
599
607
  try:
600
608
  state = self.get_acp_state()
601
-
609
+
602
610
  job = next(
603
611
  (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == job_id),
604
612
  None
@@ -611,7 +619,8 @@ class AcpPlugin:
611
619
  return FunctionResultStatus.FAILED, f"Cannot deliver - job is in '{job['phase']}' phase, must be in 'transaction' phase", {}
612
620
 
613
621
  produced = next(
614
- (i for i in self.produced_inventory if (i["jobId"] if isinstance(i, dict) else i.jobId) == job["jobId"]),
622
+ (i for i in self.produced_inventory if
623
+ (i["jobId"] if isinstance(i, dict) else i.jobId) == job["jobId"]),
615
624
  None
616
625
  )
617
626
 
@@ -645,7 +654,7 @@ class AcpPlugin:
645
654
  tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
646
655
  if tweet_id is not None:
647
656
  reply_tweet_fn = self.twitter_plugin.get_function('reply_tweet')
648
- tweet_id = reply_tweet_fn(tweet_id,tweet_content, None).get('data', {}).get('id')
657
+ tweet_id = reply_tweet_fn(tweet_id, tweet_content, None).get('data', {}).get('id')
649
658
  if tweet_id is not None:
650
- self.acp_client.add_tweet(job.get("jobId") ,tweet_id, tweet_content)
659
+ self.acp_client.add_tweet(job.get("jobId"), tweet_id, tweet_content)
651
660
  print("Tweet has been posted")
@@ -1,15 +1,18 @@
1
+ from datetime import datetime
1
2
  from enum import IntEnum
3
+ import json
2
4
  import time
5
+ import traceback
3
6
  from typing import Optional, Tuple, TypedDict
4
- from datetime import datetime
5
- from acp_plugin_gamesdk.configs import ACPContractConfig
6
- from web3 import Web3
7
+
7
8
  from eth_account import Account
8
- from acp_plugin_gamesdk.acp_token_abi import ACP_TOKEN_ABI
9
- import requests
10
9
  from eth_account.messages import encode_defunct
11
- import json
12
- import traceback
10
+ import requests
11
+ from web3 import Web3
12
+
13
+ from acp_plugin_gamesdk.acp_token_abi import ACP_TOKEN_ABI
14
+ from acp_plugin_gamesdk.configs import ACPContractConfig
15
+
13
16
 
14
17
  class MemoType(IntEnum):
15
18
  MESSAGE = 0
@@ -19,6 +22,7 @@ class MemoType(IntEnum):
19
22
  OBJECT_URL = 4
20
23
  TXHASH = 5
21
24
 
25
+
22
26
  class IMemo(TypedDict):
23
27
  content: str
24
28
  memoType: MemoType
@@ -28,6 +32,7 @@ class IMemo(TypedDict):
28
32
  numApprovals: int
29
33
  sender: str
30
34
 
35
+
31
36
  class IJob(TypedDict):
32
37
  id: int
33
38
  client: str
@@ -39,14 +44,16 @@ class IJob(TypedDict):
39
44
  expiredAt: int
40
45
  evaluatorCount: int
41
46
 
47
+
42
48
  JobResult = Tuple[int, str, str, str, str, str, str, str, int]
43
49
 
50
+
44
51
  class AcpToken:
45
52
  def __init__(
46
- self,
47
- wallet_private_key: str,
48
- agent_wallet_address: str,
49
- config: ACPContractConfig,
53
+ self,
54
+ wallet_private_key: str,
55
+ agent_wallet_address: str,
56
+ config: ACPContractConfig,
50
57
  ):
51
58
  self.web3 = Web3(Web3.HTTPProvider(config.rpc_url))
52
59
  self.account = Account.from_key(wallet_private_key)
@@ -68,7 +75,7 @@ class AcpToken:
68
75
  },
69
76
  {
70
77
  "internalType": "uint256",
71
- "name": "amount",
78
+ "name": "amount",
72
79
  "type": "uint256"
73
80
  }
74
81
  ],
@@ -86,101 +93,103 @@ class AcpToken:
86
93
  )
87
94
  self.acp_base_url = config.acp_api_url
88
95
  self.game_api_url = config.game_api_url
89
-
96
+
90
97
  def get_agent_wallet_address(self) -> str:
91
98
  return self.agent_wallet_address
92
-
99
+
93
100
  def get_contract_address(self) -> str:
94
101
  return self.contract_address
95
102
 
96
103
  def validate_transaction(self, hash_value: str) -> object:
97
104
  try:
98
- response = requests.post(f"{self.acp_base_url}/acp-agent-wallets/trx-result", json={"userOpHash": hash_value})
105
+ response = requests.post(f"{self.acp_base_url}/acp-agent-wallets/trx-result",
106
+ json={"userOpHash": hash_value})
99
107
  return response.json()
100
108
  except Exception as error:
101
109
  print(traceback.format_exc())
102
110
  raise Exception(f"Failed to get job_id {error}")
103
111
 
104
112
  def create_job(
105
- self,
106
- provider_address: str,
107
- evaluator_address: str,
108
- expire_at: datetime
113
+ self,
114
+ provider_address: str,
115
+ evaluator_address: str,
116
+ expire_at: datetime
109
117
  ) -> dict:
110
118
  try:
111
119
  provider_address = Web3.to_checksum_address(provider_address)
112
120
  evaluator_address = Web3.to_checksum_address(evaluator_address)
113
121
  expire_timestamp = int(expire_at.timestamp())
114
-
122
+
115
123
  # Sign the transaction
116
124
  trx_data, signature = self._sign_transaction(
117
- "createJob",
125
+ "createJob",
118
126
  [provider_address, evaluator_address, expire_timestamp]
119
127
  )
120
-
128
+
121
129
  # Prepare payload
122
130
  payload = {
123
131
  "agentWallet": self.get_agent_wallet_address(),
124
132
  "trxData": trx_data,
125
133
  "signature": signature
126
134
  }
127
-
135
+
128
136
  # Submit to custom API
129
137
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
130
138
  response = requests.post(api_url, json=payload)
131
-
132
-
139
+
133
140
  if response.json().get("error"):
134
- raise Exception(f"Failed to create job {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
135
-
141
+ raise Exception(
142
+ f"Failed to create job {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
143
+
136
144
  # Return transaction hash or response ID
137
145
  return {"txHash": response.json().get("data", {}).get("userOpHash", "")}
138
-
146
+
139
147
  except Exception as e:
140
148
  raise
141
149
 
142
150
  def approve_allowance(self, price_in_wei: int) -> str:
143
151
  try:
144
152
  trx_data, signature = self._sign_transaction(
145
- "approve",
153
+ "approve",
146
154
  [self.contract_address, price_in_wei],
147
155
  self.virtuals_token_address
148
156
  )
149
-
157
+
150
158
  payload = {
151
159
  "agentWallet": self.get_agent_wallet_address(),
152
160
  "trxData": trx_data,
153
161
  "signature": signature
154
162
  }
155
-
163
+
156
164
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
157
165
  response = requests.post(api_url, json=payload)
158
-
166
+
159
167
  if (response.json().get("error")):
160
- raise Exception(f"Failed to approve allowance {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
161
-
168
+ raise Exception(
169
+ f"Failed to approve allowance {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
170
+
162
171
  return response.json()
163
172
  except Exception as e:
164
173
  print(f"An error occurred while approving allowance: {e}")
165
174
  raise
166
175
 
167
176
  def create_memo(
168
- self,
169
- job_id: int,
170
- content: str,
171
- memo_type: MemoType,
172
- is_secured: bool,
173
- next_phase: int
177
+ self,
178
+ job_id: int,
179
+ content: str,
180
+ memo_type: MemoType,
181
+ is_secured: bool,
182
+ next_phase: int
174
183
  ) -> dict:
175
184
  retries = 3
176
185
  error = None
177
186
  while retries > 0:
178
187
  try:
179
188
  trx_data, signature = self._sign_transaction(
180
- "createMemo",
189
+ "createMemo",
181
190
  [job_id, content, memo_type, is_secured, next_phase]
182
191
  )
183
-
192
+
184
193
  payload = {
185
194
  "agentWallet": self.get_agent_wallet_address(),
186
195
  "trxData": trx_data,
@@ -189,99 +198,104 @@ class AcpToken:
189
198
 
190
199
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
191
200
  response = requests.post(api_url, json=payload)
192
-
201
+
193
202
  if (response.json().get("error")):
194
- raise Exception(f"Failed to create memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
195
-
196
- return { "txHash": response.json().get("txHash", response.json().get("id", "")), "memoId": response.json().get("memoId", "")}
203
+ raise Exception(
204
+ f"Failed to create memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
205
+
206
+ return {"txHash": response.json().get("txHash", response.json().get("id", "")),
207
+ "memoId": response.json().get("memoId", "")}
197
208
  except Exception as e:
198
209
  print(f"{e}")
199
210
  print(traceback.format_exc())
200
211
  error = e
201
212
  retries -= 1
202
213
  time.sleep(2 * (3 - retries))
203
-
214
+
204
215
  if error:
205
216
  raise Exception(f"{error}")
206
217
 
207
- def _sign_transaction(self, method_name: str, args: list, contract_address: Optional[str] = None) -> Tuple[dict, str]:
218
+ def _sign_transaction(self, method_name: str, args: list, contract_address: Optional[str] = None) -> Tuple[
219
+ dict, str]:
208
220
  if contract_address:
209
221
  encoded_data = self.virtuals_token_contract.encode_abi(method_name, args=args)
210
222
  else:
211
223
  encoded_data = self.contract.encode_abi(method_name, args=args)
212
-
224
+
213
225
  trx_data = {
214
226
  "target": contract_address if contract_address else self.get_contract_address(),
215
227
  "value": "0",
216
228
  "data": encoded_data
217
229
  }
218
-
230
+
219
231
  message_json = json.dumps(trx_data, separators=(",", ":"), sort_keys=False)
220
232
  message_bytes = message_json.encode()
221
-
233
+
222
234
  # Sign the transaction
223
235
  message = encode_defunct(message_bytes)
224
236
  signature = "0x" + self.account.sign_message(message).signature.hex()
225
-
237
+
226
238
  return trx_data, signature
227
239
 
228
240
  def sign_memo(
229
- self,
230
- memo_id: int,
231
- is_approved: bool,
232
- reason: Optional[str] = ""
241
+ self,
242
+ memo_id: int,
243
+ is_approved: bool,
244
+ reason: Optional[str] = ""
233
245
  ) -> str:
234
246
  retries = 3
235
247
  error = None
236
248
  while retries > 0:
237
249
  try:
238
250
  trx_data, signature = self._sign_transaction(
239
- "signMemo",
251
+ "signMemo",
240
252
  [memo_id, is_approved, reason]
241
253
  )
242
-
254
+
243
255
  payload = {
244
256
  "agentWallet": self.get_agent_wallet_address(),
245
257
  "trxData": trx_data,
246
258
  "signature": signature
247
259
  }
248
-
260
+
249
261
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
250
262
  response = requests.post(api_url, json=payload)
251
-
263
+
252
264
  if (response.json().get("error")):
253
- raise Exception(f"Failed to sign memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
254
-
265
+ raise Exception(
266
+ f"Failed to sign memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
267
+
255
268
  return response.json()
256
-
269
+
257
270
  except Exception as e:
258
271
  error = e
259
272
  print(f"{error}")
260
273
  print(traceback.format_exc())
261
274
  retries -= 1
262
275
  time.sleep(2 * (3 - retries))
263
-
276
+
264
277
  raise Exception(f"Failed to sign memo {error}")
265
278
 
266
279
  def set_budget(self, job_id: int, budget: int) -> str:
267
280
  try:
268
281
  trx_data, signature = self._sign_transaction(
269
- "setBudget",
282
+ "setBudget",
270
283
  [job_id, budget]
271
284
  )
272
-
285
+
273
286
  payload = {
274
287
  "agentWallet": self.get_agent_wallet_address(),
275
288
  "trxData": trx_data,
276
289
  "signature": signature
277
290
  }
278
-
291
+
279
292
  api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
280
293
  response = requests.post(api_url, json=payload)
281
-
294
+
282
295
  if (response.json().get("error")):
283
- raise Exception(f"Failed to set budget {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
284
-
296
+ raise Exception(
297
+ f"Failed to set budget {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
298
+
285
299
  return response.json()
286
300
  except Exception as error:
287
301
  raise Exception(f"{error}")
@@ -289,10 +303,10 @@ class AcpToken:
289
303
  def get_job(self, job_id: int) -> Optional[IJob]:
290
304
  try:
291
305
  job_data = self.contract.functions.jobs(job_id).call()
292
-
306
+
293
307
  if not job_data:
294
308
  return None
295
-
309
+
296
310
  return {
297
311
  'id': job_data[0],
298
312
  'client': job_data[1],
@@ -308,13 +322,13 @@ class AcpToken:
308
322
  raise Exception(f"{error}")
309
323
 
310
324
  def get_memo_by_job(
311
- self,
312
- job_id: int,
313
- memo_type: Optional[MemoType] = None
325
+ self,
326
+ job_id: int,
327
+ memo_type: Optional[MemoType] = None
314
328
  ) -> Optional[IMemo]:
315
329
  try:
316
330
  memos = self.contract.functions.getAllMemos(job_id).call()
317
-
331
+
318
332
  if memo_type is not None:
319
333
  filtered_memos = [m for m in memos if m['memoType'] == memo_type]
320
334
  return filtered_memos[-1] if filtered_memos else None
@@ -324,14 +338,14 @@ class AcpToken:
324
338
  raise Exception(f"Failed to get memo by job {error}")
325
339
 
326
340
  def get_memos_for_phase(
327
- self,
328
- job_id: int,
329
- phase: int,
330
- target_phase: int
341
+ self,
342
+ job_id: int,
343
+ phase: int,
344
+ target_phase: int
331
345
  ) -> Optional[IMemo]:
332
346
  try:
333
347
  memos = self.contract.functions.getMemosForPhase(job_id, phase).call()
334
-
348
+
335
349
  target_memos = [m for m in memos if m['nextPhase'] == target_phase]
336
350
  return target_memos[-1] if target_memos else None
337
351
  except Exception as error:
@@ -3,6 +3,7 @@ from typing import Literal
3
3
 
4
4
  ChainEnv = Literal["base-sepolia", "base"]
5
5
 
6
+
6
7
  @dataclass
7
8
  class ACPContractConfig:
8
9
  chain_env: ChainEnv
@@ -13,6 +14,7 @@ class ACPContractConfig:
13
14
  acp_api_url: str
14
15
  game_api_url: str
15
16
 
17
+
16
18
  # Configuration for Base Sepolia
17
19
  BASE_SEPOLIA_CONFIG = ACPContractConfig(
18
20
  chain_env="base-sepolia",
@@ -27,16 +29,16 @@ BASE_SEPOLIA_CONFIG = ACPContractConfig(
27
29
  # Configuration for Base Mainnet
28
30
  BASE_MAINNET_CONFIG = ACPContractConfig(
29
31
  chain_env="base",
30
- rpc_url="https://mainnet.base.org",
32
+ rpc_url="https://mainnet.base.org",
31
33
  chain_id=8453,
32
34
  contract_address="0x6a1FE26D54ab0d3E1e3168f2e0c0cDa5cC0A0A4A",
33
35
  virtuals_token_address="0x0b3e328455c4059EEb9e3f84b5543F74E24e7E1b",
34
- acp_api_url="https://acpx.virtuals.io/api", # PROD
36
+ acp_api_url="https://acpx.virtuals.io/api", # PROD
35
37
  game_api_url="https://sdk.game.virtuals.io"
36
38
  )
37
39
 
38
40
  # Define the default configuration for the SDK
39
41
  # For a production-ready SDK, this would typically be BASE_MAINNET_CONFIG.
40
42
  # For initial development/testing, BASE_SEPOLIA_CONFIG might be more appropriate.
41
- DEFAULT_CONFIG = BASE_MAINNET_CONFIG
43
+ DEFAULT_CONFIG = BASE_MAINNET_CONFIG
42
44
  # Or: DEFAULT_CONFIG = BASE_SEPOLIA_CONFIG
@@ -1,6 +1,7 @@
1
1
  from dataclasses import dataclass
2
- from enum import IntEnum, Enum
3
- from typing import List, Literal, Optional, Callable
2
+ from enum import Enum, IntEnum
3
+ from typing import Any, Callable, Dict, List, Literal, Optional, Union
4
+
4
5
 
5
6
  @dataclass
6
7
  class AcpOffering:
@@ -12,7 +13,8 @@ class AcpOffering:
12
13
  f"Offering(name={self.name}, price={self.price})"
13
14
  )
14
15
  return output
15
-
16
+
17
+
16
18
  @dataclass
17
19
  class AcpAgent:
18
20
  id: str
@@ -28,7 +30,7 @@ class AcpAgent:
28
30
  offer = ""
29
31
  if self.offerings:
30
32
  for index, off in enumerate(self.offerings):
31
- offer += f"{index+1}. {str(off)}\n"
33
+ offer += f"{index + 1}. {str(off)}\n"
32
34
 
33
35
  output = (
34
36
  f"😎 Agent ID={self.id}\n"
@@ -38,7 +40,8 @@ class AcpAgent:
38
40
  f"Explanation:\n{self.explanation}"
39
41
  )
40
42
  return output
41
-
43
+
44
+
42
45
  class AcpJobPhases(IntEnum):
43
46
  REQUEST = 0
44
47
  NEGOTIATION = 1
@@ -47,6 +50,7 @@ class AcpJobPhases(IntEnum):
47
50
  COMPLETED = 4
48
51
  REJECTED = 5
49
52
 
53
+
50
54
  class AcpJobPhasesDesc(str, Enum):
51
55
  REQUEST = "request"
52
56
  NEGOTIATION = "pending_payment"
@@ -55,6 +59,7 @@ class AcpJobPhasesDesc(str, Enum):
55
59
  COMPLETED = "completed"
56
60
  REJECTED = "rejected"
57
61
 
62
+
58
63
  @dataclass
59
64
  class AcpRequestMemo:
60
65
  id: int
@@ -63,7 +68,8 @@ class AcpRequestMemo:
63
68
  def __repr__(self) -> str:
64
69
  output = f"Memo(ID: {self.id}, created at: {self.createdAt})"
65
70
  return output
66
-
71
+
72
+
67
73
  @dataclass
68
74
  class ITweet:
69
75
  type: Literal["buyer", "seller"]
@@ -71,24 +77,24 @@ class ITweet:
71
77
  content: str
72
78
  created_at: int
73
79
 
80
+
74
81
  @dataclass
75
82
  class AcpJob:
76
83
  jobId: Optional[int]
77
- clientName : Optional[str]
84
+ clientName: Optional[str]
78
85
  providerName: Optional[str]
79
- desc: str
80
- price: str
86
+ desc: Optional[str]
87
+ price: float
81
88
  providerAddress: Optional[str]
82
89
  clientAddress: Optional[str]
83
90
  phase: AcpJobPhasesDesc
84
91
  memo: List[AcpRequestMemo]
85
- tweetHistory : ITweet | List
92
+ tweetHistory: ITweet | List
86
93
  lastUpdated: int
87
94
  getAgentByWalletAddress: Optional[Callable[[str], AcpAgent]]
88
-
89
95
 
90
96
  def __repr__(self) -> str:
91
- output =(
97
+ output = (
92
98
  f"Job ID: {self.jobId}, "
93
99
  f"Client Name: {self.clientName}, "
94
100
  f"Provider Name: {self.providerName}, "
@@ -100,21 +106,25 @@ class AcpJob:
100
106
  f"Memo: {self.memo}, "
101
107
  f"Tweet History: {self.tweetHistory}, "
102
108
  f"Last Updated: {self.lastUpdated})"
103
- )
109
+ )
104
110
  return output
105
111
 
112
+
106
113
  @dataclass
107
114
  class IDeliverable:
108
115
  type: str
109
- value: str
116
+ value: Union[str, Dict[str, Any], List[Any]]
110
117
  clientName: Optional[str]
111
118
  providerName: Optional[str]
119
+
120
+
112
121
  @dataclass
113
122
  class IInventory(IDeliverable):
114
123
  jobId: int
115
124
  clientName: Optional[str]
116
125
  providerName: Optional[str]
117
126
 
127
+
118
128
  @dataclass
119
129
  class AcpJobsSection:
120
130
  asABuyer: List[AcpJob]
@@ -123,11 +133,11 @@ class AcpJobsSection:
123
133
  def __str__(self) -> str:
124
134
  buyer_jobs = ""
125
135
  for index, job in enumerate(self.asABuyer):
126
- buyer_jobs += f"#{index+1} {str(job)} \n"
136
+ buyer_jobs += f"#{index + 1} {str(job)} \n"
127
137
 
128
138
  seller_jobs = ""
129
139
  for index, job in enumerate(self.asASeller):
130
- seller_jobs += f"#{index+1} {str(job)} \n"
140
+ seller_jobs += f"#{index + 1} {str(job)} \n"
131
141
 
132
142
  output = (
133
143
  f"As Buyer:\n{buyer_jobs}\n"
@@ -135,6 +145,7 @@ class AcpJobsSection:
135
145
  )
136
146
  return output
137
147
 
148
+
138
149
  @dataclass
139
150
  class AcpJobs:
140
151
  active: AcpJobsSection
@@ -149,7 +160,8 @@ class AcpJobs:
149
160
  f"🔴 Cancelled:\n{self.cancelled}\n"
150
161
  )
151
162
  return output
152
-
163
+
164
+
153
165
  @dataclass
154
166
  class AcpInventory:
155
167
  acquired: List[IInventory]
@@ -163,6 +175,7 @@ class AcpInventory:
163
175
  )
164
176
  return output
165
177
 
178
+
166
179
  @dataclass
167
180
  class AcpState:
168
181
  inventory: AcpInventory
@@ -170,9 +183,9 @@ class AcpState:
170
183
 
171
184
  def __str__(self) -> str:
172
185
  output = (
173
- f"🤖 Agent State".center(50, '=') + "\n" + \
174
- f"{str(self.inventory)}\n" + \
175
- f"{str(self.jobs)}\n" + \
176
- f"State End".center(50, '=') + "\n"
186
+ f"🤖 Agent State".center(50, '=') + "\n" + \
187
+ f"{str(self.inventory)}\n" + \
188
+ f"{str(self.jobs)}\n" + \
189
+ f"State End".center(50, '=') + "\n"
177
190
  )
178
- return output
191
+ return output
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: acp-plugin-gamesdk
3
- Version: 0.1.19
3
+ Version: 0.1.21
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
@@ -0,0 +1,9 @@
1
+ acp_plugin_gamesdk/acp_client.py,sha256=fsSByXElEJuzFdhJTphHmtRv8Tqhs5eXuCGRm2l2JtM,10167
2
+ acp_plugin_gamesdk/acp_plugin.py,sha256=kJ0S_0Fzx0IHdKnAEHA08ZmLIIyv4URgEBRjdCyFx0I,27022
3
+ acp_plugin_gamesdk/acp_token.py,sha256=zjW-W7_4JO31K8eLBBw5dn5tb4FrCSag6qGYQi7xkaA,11804
4
+ acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
+ acp_plugin_gamesdk/configs.py,sha256=4rLOOMbRPqf2RM-Lz5Az7mbUI_a5kgmySZlBiMkB3Mo,1406
6
+ acp_plugin_gamesdk/interface.py,sha256=WkeI0YLaMOGt5vYJzwuq5SuviaZKfwePQGhPE8HaU8w,4561
7
+ acp_plugin_gamesdk-0.1.21.dist-info/METADATA,sha256=PzI39ny2YjUrwZgUQ89v37kfClx-_GaAw6bLIhKwNdI,12791
8
+ acp_plugin_gamesdk-0.1.21.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
9
+ acp_plugin_gamesdk-0.1.21.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- acp_plugin_gamesdk/acp_client.py,sha256=ScJc6Sqly8xrsvGWbEvRBeMFuLRS1UsZxgESJTfyBmw,10468
2
- acp_plugin_gamesdk/acp_plugin.py,sha256=G55hy2-PPzyaJOL3Pb3WLUdQA5hgI9KxHgtJinB7FFQ,27357
3
- acp_plugin_gamesdk/acp_token.py,sha256=LE60bHpJDsR0F-5mKxrhXVMyHIEHG0-BvjCRLaJoUJI,11954
4
- acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
- acp_plugin_gamesdk/configs.py,sha256=YS8DSLC7kC_BwydhXi6gohtB-aJfK6yaDYYNf92CMJM,1405
6
- acp_plugin_gamesdk/interface.py,sha256=rb4YnWmOZXE5iqfG9bHyJeTnF1oml_eU9DiDqgZLdaQ,4483
7
- acp_plugin_gamesdk-0.1.19.dist-info/METADATA,sha256=AL0rkcvKmFEyj35znPhh_ApcWAL1248ePD0Erb3dh4c,12791
8
- acp_plugin_gamesdk-0.1.19.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
9
- acp_plugin_gamesdk-0.1.19.dist-info/RECORD,,