acp-plugin-gamesdk 0.1.18__py3-none-any.whl → 0.1.20__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,39 +234,75 @@ 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
  )
272
+
273
+ def get_agent_by_wallet_address(self, wallet_address: str) -> AcpAgent:
274
+ url = f"{self.acp_base_url}/agents?filters[walletAddress]={wallet_address}"
275
+
276
+ response = requests.get(
277
+ url,
278
+ )
279
+
280
+ if response.status_code != 200:
281
+ raise Exception(
282
+ f"Failed to get agent: {response.status_code} {response.text}"
283
+ )
284
+
285
+ response_json = response.json()
286
+
287
+ result = []
288
+
289
+ for agent in response_json.get("data", []):
290
+ if agent["offerings"]:
291
+ offerings = [AcpOffering(name=offering["name"], price=offering["price"]) for offering in
292
+ agent["offerings"]]
293
+ else:
294
+ offerings = None
295
+
296
+ result.append(
297
+ AcpAgent(
298
+ id=agent["id"],
299
+ name=agent["name"],
300
+ twitter_handle=agent["twitterHandle"],
301
+ description=agent["description"],
302
+ wallet_address=agent["walletAddress"],
303
+ offerings=offerings,
304
+ score=0,
305
+ explanation=""
306
+ )
307
+ )
308
+ return result[0]
@@ -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
-
13
11
  from game_sdk.game.agent import WorkerConfig
14
12
  from game_sdk.game.custom_types import Argument, Function, FunctionResultStatus
15
- from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
16
13
  from twitter_plugin_gamesdk.game_twitter_plugin import GameTwitterPlugin
14
+ from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
15
+
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,12 +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
- self.on_phase_change = options.on_phase_change
83
+ def phase_change_wrapper(job: AcpJob):
84
+ job["getAgentByWalletAddress"] = self.acp_client.get_agent_by_wallet_address
85
+ return options.on_phase_change(job)
86
+
87
+ self.on_phase_change = phase_change_wrapper
78
88
  self.initialize_socket()
79
89
  self.job_expiry_duration_mins = options.job_expiry_duration_mins if options.job_expiry_duration_mins is not None else 1440
80
-
81
-
82
-
90
+
83
91
  def initialize_socket(self) -> Tuple[bool, str]:
84
92
  """
85
93
  Initialize socket connection for real-time communication.
@@ -87,37 +95,36 @@ class AcpPlugin:
87
95
  """
88
96
  try:
89
97
  self.socket = socketio.Client()
90
-
98
+
91
99
  # Set up authentication before connecting
92
100
  self.socket.auth = {
93
101
  "evaluatorAddress": self.acp_token_client.agent_wallet_address
94
102
  }
95
-
103
+
96
104
  # Connect socket to GAME SDK dev server
97
105
  self.socket.connect(self.acp_client.base_url, auth=self.socket.auth)
98
-
106
+
99
107
  if self.socket.connected:
100
108
  self.socket.emit(SocketEvents["JOIN_EVALUATOR_ROOM"], self.acp_token_client.agent_wallet_address)
101
-
102
-
109
+
103
110
  # Set up event handler for evaluation requests
104
111
  @self.socket.on(SocketEvents["ON_EVALUATE"])
105
112
  def on_evaluate(data):
106
113
  if self.on_evaluate:
107
114
  deliverable = data.get("deliverable")
108
115
  memo_id = data.get("memoId")
109
-
116
+
110
117
  is_approved, reasoning = self.on_evaluate(deliverable)
111
-
118
+
112
119
  self.acp_token_client.sign_memo(memo_id, is_approved, reasoning)
113
-
114
- # Set up event handler for phase changes
120
+
121
+ # Set up event handler for phase changes
122
+
115
123
  @self.socket.on(SocketEvents["ON_PHASE_CHANGE"])
116
124
  def on_phase_change(data):
117
125
  if hasattr(self, 'on_phase_change') and self.on_phase_change:
118
- print(f"on_phase_change: {data}")
119
126
  self.on_phase_change(data)
120
-
127
+
121
128
  # Set up cleanup function for graceful shutdown
122
129
  def cleanup():
123
130
  if self.socket:
@@ -125,34 +132,31 @@ class AcpPlugin:
125
132
  import time
126
133
  time.sleep(1)
127
134
  self.socket.disconnect()
128
-
129
-
130
-
135
+
131
136
  def signal_handler(_sig, _frame):
132
137
  cleanup()
133
138
  sys.exit(0)
134
-
139
+
135
140
  signal.signal(signal.SIGINT, signal_handler)
136
141
  signal.signal(signal.SIGTERM, signal_handler)
137
-
142
+
138
143
  return True, "Socket initialized successfully"
139
-
144
+
140
145
  except Exception as e:
141
146
  return False, f"Failed to initialize socket: {str(e)}"
142
-
143
-
147
+
144
148
  def set_on_phase_change(self, on_phase_change: Callable[[AcpJob], None]) -> None:
145
149
  self.on_phase_change = on_phase_change
146
150
 
147
151
  def add_produce_item(self, item: IInventory) -> None:
148
152
  self.produced_inventory.append(item)
149
-
153
+
150
154
  def reset_state(self) -> None:
151
155
  self.acp_client.reset_state()
152
-
156
+
153
157
  def delete_completed_job(self, job_id: int) -> None:
154
158
  self.acp_client.delete_completed_job(job_id)
155
-
159
+
156
160
  def get_acp_state(self) -> Dict:
157
161
  server_state = self.acp_client.get_state()
158
162
  server_state.inventory.produced = self.produced_inventory
@@ -167,7 +171,7 @@ class AcpPlugin:
167
171
  self.pay_job,
168
172
  self.deliver_job,
169
173
  ]
170
-
174
+
171
175
  def get_environment(_function_result, _current_state) -> Dict[str, Any]:
172
176
  environment = data.get_environment() if hasattr(data, "get_environment") else {}
173
177
  return {
@@ -182,7 +186,7 @@ class AcpPlugin:
182
186
  get_state_fn=get_environment,
183
187
  instruction=data.get("instructions") if data else None
184
188
  )
185
-
189
+
186
190
  return worker_config
187
191
 
188
192
  @property
@@ -201,8 +205,8 @@ class AcpPlugin:
201
205
  - Each job tracks:
202
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
203
207
  """
204
-
205
- 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]:
206
210
  if not reasoning:
207
211
  return FunctionResultStatus.FAILED, "Reasoning for the search must be provided. This helps track your decision-making process for future reference.", {}
208
212
 
@@ -289,21 +293,22 @@ class AcpPlugin:
289
293
  type="string",
290
294
  description="Detailed specifications for service-based items",
291
295
  )
292
-
296
+
293
297
  require_evaluation_arg = Argument(
294
298
  name="require_evaluation",
295
299
  type="boolean",
296
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.",
297
301
  )
298
-
302
+
299
303
  evaluator_keyword_arg = Argument(
300
304
  name="evaluator_keyword",
301
305
  type="string",
302
306
  description="Keyword to search for a evaluator",
303
307
  )
304
308
 
305
- args = [seller_wallet_address_arg, price_arg, reasoning_arg, service_requirements_arg, require_evaluation_arg, evaluator_keyword_arg]
306
-
309
+ args = [seller_wallet_address_arg, price_arg, reasoning_arg, service_requirements_arg, require_evaluation_arg,
310
+ evaluator_keyword_arg]
311
+
307
312
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
308
313
  tweet_content_arg = Argument(
309
314
  name="tweet_content",
@@ -311,7 +316,7 @@ class AcpPlugin:
311
316
  description="Tweet content that will be posted about this job. Must include the seller's Twitter handle (with @ symbol) to notify them",
312
317
  )
313
318
  args.append(tweet_content_arg)
314
-
319
+
315
320
  return Function(
316
321
  fn_name="initiate_job",
317
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.",
@@ -319,7 +324,9 @@ class AcpPlugin:
319
324
  executable=self._initiate_job_executable
320
325
  )
321
326
 
322
- 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]:
323
330
  if isinstance(require_evaluation, str):
324
331
  require_evaluation = require_evaluation.lower() == 'true'
325
332
  elif isinstance(require_evaluation, bool):
@@ -329,10 +336,10 @@ class AcpPlugin:
329
336
 
330
337
  if not price:
331
338
  return FunctionResultStatus.FAILED, "Missing price - specify how much you're offering per unit", {}
332
-
339
+
333
340
  if not reasoning:
334
341
  return FunctionResultStatus.FAILED, "Missing reasoning - explain why you're making this purchase request", {}
335
-
342
+
336
343
  try:
337
344
  state = self.get_acp_state()
338
345
 
@@ -343,24 +350,25 @@ class AcpPlugin:
343
350
  )
344
351
 
345
352
  if existing_job:
346
- 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", {}
347
-
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
+
348
355
  if not seller_wallet_address:
349
356
  return FunctionResultStatus.FAILED, "Missing seller wallet address - specify the agent you want to buy from", {}
350
-
357
+
351
358
  if require_evaluation and not evaluator_keyword:
352
359
  return FunctionResultStatus.FAILED, "Missing validator keyword - provide a keyword to search for a validator", {}
353
-
360
+
354
361
  evaluator_address = self.acp_token_client.get_agent_wallet_address()
355
-
362
+
356
363
  if require_evaluation:
357
- validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluator_keyword, rerank=True, top_k=1)
358
-
364
+ validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluator_keyword, rerank=True,
365
+ top_k=1)
366
+
359
367
  if len(validators) == 0:
360
368
  return FunctionResultStatus.FAILED, "No evaluator found - try a different keyword", {}
361
369
 
362
370
  evaluator_address = validators[0].wallet_address
363
-
371
+
364
372
  # ... Rest of validation logic ...
365
373
  expired_at = datetime.now(timezone.utc) + timedelta(minutes=self.job_expiry_duration_mins)
366
374
  job_id = self.acp_client.create_job(
@@ -408,9 +416,9 @@ class AcpPlugin:
408
416
  type="string",
409
417
  description="Why you made this decision",
410
418
  )
411
-
419
+
412
420
  args = [job_id_arg, decision_arg, reasoning_arg]
413
-
421
+
414
422
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
415
423
  tweet_content_arg = Argument(
416
424
  name="tweet_content",
@@ -426,19 +434,20 @@ class AcpPlugin:
426
434
  executable=self._respond_job_executable
427
435
  )
428
436
 
429
- 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]:
430
439
  if not job_id:
431
440
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're responding to", {}
432
-
441
+
433
442
  if not decision or decision not in ["ACCEPT", "REJECT"]:
434
443
  return FunctionResultStatus.FAILED, "Invalid decision - must be either 'ACCEPT' or 'REJECT'", {}
435
-
444
+
436
445
  if not reasoning:
437
446
  return FunctionResultStatus.FAILED, "Missing reasoning - explain why you made this decision", {}
438
447
 
439
448
  try:
440
449
  state = self.get_acp_state()
441
-
450
+
442
451
  job = next(
443
452
  (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == job_id),
444
453
  None
@@ -488,7 +497,7 @@ class AcpPlugin:
488
497
  )
489
498
 
490
499
  args = [job_id_arg, amount_arg, reasoning_arg]
491
-
500
+
492
501
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
493
502
  tweet_content_arg = Argument(
494
503
  name="tweet_content",
@@ -504,7 +513,8 @@ class AcpPlugin:
504
513
  executable=self._pay_job_executable
505
514
  )
506
515
 
507
- 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]:
508
518
  if not job_id:
509
519
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're paying for", {}
510
520
 
@@ -516,7 +526,7 @@ class AcpPlugin:
516
526
 
517
527
  try:
518
528
  state = self.get_acp_state()
519
-
529
+
520
530
  job = next(
521
531
  (c for c in state["jobs"]["active"]["asABuyer"] if c["jobId"] == job_id),
522
532
  None
@@ -528,7 +538,6 @@ class AcpPlugin:
528
538
  if job["phase"] != AcpJobPhasesDesc.NEGOTIATION:
529
539
  return FunctionResultStatus.FAILED, f"Cannot pay - job is in '{job['phase']}' phase, must be in 'negotiation' phase", {}
530
540
 
531
-
532
541
  self.acp_client.make_payment(
533
542
  job_id,
534
543
  amount,
@@ -568,7 +577,7 @@ class AcpPlugin:
568
577
  )
569
578
 
570
579
  args = [job_id_arg, deliverable_arg, reasoning_arg]
571
-
580
+
572
581
  if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
573
582
  tweet_content_arg = Argument(
574
583
  name="tweet_content",
@@ -584,19 +593,20 @@ class AcpPlugin:
584
593
  executable=self._deliver_job_executable
585
594
  )
586
595
 
587
- 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]:
588
598
  if not job_id:
589
599
  return FunctionResultStatus.FAILED, "Missing job ID - specify which job you're delivering for", {}
590
-
600
+
591
601
  if not reasoning:
592
602
  return FunctionResultStatus.FAILED, "Missing reasoning - explain why you're making this delivery", {}
593
-
603
+
594
604
  if not deliverable:
595
605
  return FunctionResultStatus.FAILED, "Missing deliverable - specify what you're delivering", {}
596
606
 
597
607
  try:
598
608
  state = self.get_acp_state()
599
-
609
+
600
610
  job = next(
601
611
  (c for c in state["jobs"]["active"]["asASeller"] if c["jobId"] == job_id),
602
612
  None
@@ -609,7 +619,8 @@ class AcpPlugin:
609
619
  return FunctionResultStatus.FAILED, f"Cannot deliver - job is in '{job['phase']}' phase, must be in 'transaction' phase", {}
610
620
 
611
621
  produced = next(
612
- (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"]),
613
624
  None
614
625
  )
615
626
 
@@ -643,7 +654,7 @@ class AcpPlugin:
643
654
  tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
644
655
  if tweet_id is not None:
645
656
  reply_tweet_fn = self.twitter_plugin.get_function('reply_tweet')
646
- 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')
647
658
  if tweet_id is not None:
648
- 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)
649
660
  print("Tweet has been posted")
@@ -1,15 +1,18 @@
1
- from enum import IntEnum
1
+ import json
2
2
  import time
3
- from typing import Optional, Tuple, TypedDict
3
+ import traceback
4
4
  from datetime import datetime
5
- from acp_plugin_gamesdk.configs import ACPContractConfig
6
- from web3 import Web3
7
- from eth_account import Account
8
- from acp_plugin_gamesdk.acp_token_abi import ACP_TOKEN_ABI
5
+ from enum import IntEnum
6
+ from typing import Optional, Tuple, TypedDict
7
+
9
8
  import requests
9
+ from eth_account import Account
10
10
  from eth_account.messages import encode_defunct
11
- import json
12
- import traceback
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
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,46 +77,54 @@ 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
86
  desc: str
80
87
  price: str
81
88
  providerAddress: Optional[str]
89
+ clientAddress: Optional[str]
82
90
  phase: AcpJobPhasesDesc
83
91
  memo: List[AcpRequestMemo]
84
- tweetHistory : ITweet | List
92
+ tweetHistory: ITweet | List
85
93
  lastUpdated: int
94
+ getAgentByWalletAddress: Optional[Callable[[str], AcpAgent]]
86
95
 
87
96
  def __repr__(self) -> str:
88
- output =(
97
+ output = (
89
98
  f"Job ID: {self.jobId}, "
90
99
  f"Client Name: {self.clientName}, "
91
100
  f"Provider Name: {self.providerName}, "
92
101
  f"Description: {self.desc}, "
93
102
  f"Price: {self.price}, "
94
103
  f"Provider Address: {self.providerAddress}, "
104
+ f"Client Address: {self.clientAddress}, "
95
105
  f"Phase: {self.phase.value}, "
96
106
  f"Memo: {self.memo}, "
97
107
  f"Tweet History: {self.tweetHistory}, "
98
108
  f"Last Updated: {self.lastUpdated})"
99
- )
109
+ )
100
110
  return output
101
111
 
112
+
102
113
  @dataclass
103
114
  class IDeliverable:
104
115
  type: str
105
- value: str
116
+ value: Union[str, Dict[str, Any], List[Any]]
106
117
  clientName: Optional[str]
107
118
  providerName: Optional[str]
119
+
120
+
108
121
  @dataclass
109
122
  class IInventory(IDeliverable):
110
123
  jobId: int
111
124
  clientName: Optional[str]
112
125
  providerName: Optional[str]
113
126
 
127
+
114
128
  @dataclass
115
129
  class AcpJobsSection:
116
130
  asABuyer: List[AcpJob]
@@ -119,11 +133,11 @@ class AcpJobsSection:
119
133
  def __str__(self) -> str:
120
134
  buyer_jobs = ""
121
135
  for index, job in enumerate(self.asABuyer):
122
- buyer_jobs += f"#{index+1} {str(job)} \n"
136
+ buyer_jobs += f"#{index + 1} {str(job)} \n"
123
137
 
124
138
  seller_jobs = ""
125
139
  for index, job in enumerate(self.asASeller):
126
- seller_jobs += f"#{index+1} {str(job)} \n"
140
+ seller_jobs += f"#{index + 1} {str(job)} \n"
127
141
 
128
142
  output = (
129
143
  f"As Buyer:\n{buyer_jobs}\n"
@@ -131,6 +145,7 @@ class AcpJobsSection:
131
145
  )
132
146
  return output
133
147
 
148
+
134
149
  @dataclass
135
150
  class AcpJobs:
136
151
  active: AcpJobsSection
@@ -145,7 +160,8 @@ class AcpJobs:
145
160
  f"🔴 Cancelled:\n{self.cancelled}\n"
146
161
  )
147
162
  return output
148
-
163
+
164
+
149
165
  @dataclass
150
166
  class AcpInventory:
151
167
  acquired: List[IInventory]
@@ -159,6 +175,7 @@ class AcpInventory:
159
175
  )
160
176
  return output
161
177
 
178
+
162
179
  @dataclass
163
180
  class AcpState:
164
181
  inventory: AcpInventory
@@ -166,9 +183,9 @@ class AcpState:
166
183
 
167
184
  def __str__(self) -> str:
168
185
  output = (
169
- f"🤖 Agent State".center(50, '=') + "\n" + \
170
- f"{str(self.inventory)}\n" + \
171
- f"{str(self.jobs)}\n" + \
172
- 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"
173
190
  )
174
- 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.18
3
+ Version: 0.1.20
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=4-flS9WnQqvwX1mhJJ2SiDmGuQs_sIiec-1fFlipGHk,27022
3
+ acp_plugin_gamesdk/acp_token.py,sha256=uOeQ6_azL50sJekDlWYjwAtd2YxedDXjRuoCRLFZJ8Q,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=0u3exP_MXHjTF4rrAPQAkGAtSGCaOvhfXewepDr9Hb8,4549
7
+ acp_plugin_gamesdk-0.1.20.dist-info/METADATA,sha256=Uh-DfnSzyRgxWJnite0txHh2Qqhz9KKDdOioEiB4pGc,12791
8
+ acp_plugin_gamesdk-0.1.20.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
9
+ acp_plugin_gamesdk-0.1.20.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- acp_plugin_gamesdk/acp_client.py,sha256=gaTZ6H7tRlz0dDyodhb-KhbzcYQj-KwPJA8YTAqvYWk,9221
2
- acp_plugin_gamesdk/acp_plugin.py,sha256=g-DHfhiWmFmvKlXYlNaLaKmQLjXnJ42zDSafONKtJic,27205
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=jC6xXDsL8agw9flbszkYPxd01qER2vR2ukQnLJMHYKI,4316
7
- acp_plugin_gamesdk-0.1.18.dist-info/METADATA,sha256=fkFk_092a90AHCUzQHmQSkc44bp_KE9p028KbtOHOhM,12791
8
- acp_plugin_gamesdk-0.1.18.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
9
- acp_plugin_gamesdk-0.1.18.dist-info/RECORD,,