acp-plugin-gamesdk 0.1.6__py3-none-any.whl → 0.1.8__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,12 +1,16 @@
1
- from datetime import datetime, timedelta
2
- from typing import List, Optional
3
- from web3 import Web3
1
+ import json
4
2
  import requests
5
- from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpOffering, AcpState
6
- from acp_plugin_gamesdk.acp_token import AcpToken, MemoType
7
3
  import time
8
4
  import traceback
9
5
 
6
+ from datetime import datetime, timedelta, timezone
7
+ from typing import List, Optional
8
+ from web3 import Web3
9
+
10
+ from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpOffering, AcpState, AcpJobPhasesDesc
11
+ from acp_plugin_gamesdk.acp_token import AcpToken, MemoType
12
+ from dacite import from_dict, Config
13
+
10
14
 
11
15
  class AcpClient:
12
16
  def __init__(self, api_key: str, acp_token: AcpToken, acp_base_url: Optional[str] = None):
@@ -25,21 +29,29 @@ class AcpClient:
25
29
  f"{self.base_url}/states/{self.agent_wallet_address}",
26
30
  headers={"x-api-key": self.api_key}
27
31
  )
28
- return response.json()
32
+ payload = response.json()
33
+ result = from_dict(data_class=AcpState, data=payload, config=Config(type_hooks={AcpJobPhasesDesc: AcpJobPhasesDesc}))
34
+ return result
29
35
 
30
- def browse_agents(self, cluster: Optional[str] = None, query: Optional[str] = None) -> List[AcpAgent]:
31
- url = f"{self.acp_base_url}/agents"
36
+ def browse_agents(
37
+ self,
38
+ cluster: Optional[str] = None,
39
+ query: Optional[str] = None,
40
+ rerank: Optional[bool] = True,
41
+ top_k: Optional[int] = 1,
42
+ ) -> List[AcpAgent]:
32
43
 
33
- if query:
34
- url += f"?search={requests.utils.quote(query)}"
35
-
36
- if cluster:
37
- # Add & if there's already a parameter, otherwise add ?
38
- separator = "&" if query else "?"
39
- url += f"{separator}filters[cluster]={requests.utils.quote(cluster)}"
44
+ url = f"{self.acp_base_url}/agents"
45
+
46
+ params = {
47
+ "search": query,
48
+ "filters[cluster]": cluster,
49
+ "filters[walletAddress][$notIn]": self.agent_wallet_address,
50
+ "rerank": "true" if rerank else "false",
51
+ "top_k": top_k,
52
+ }
53
+ response = requests.get(url, params=params)
40
54
 
41
- response = requests.get(url)
42
-
43
55
  if response.status_code != 200:
44
56
  raise Exception(
45
57
  f"Error occured in browse_agents function. Failed to browse agents.\n"
@@ -64,19 +76,26 @@ class AcpClient:
64
76
  name=agent["name"],
65
77
  description=agent["description"],
66
78
  wallet_address=agent["walletAddress"],
67
- offerings=offerings
79
+ offerings=offerings,
80
+ score=agent["score"],
81
+ explanation=agent["explanation"]
68
82
  )
69
83
  )
70
84
 
71
85
  return result
72
86
 
73
- def create_job(self, provider_address: str, price: float, job_description: str, evaluator_address: str) -> int:
74
- expire_at = datetime.now() + timedelta(days=1)
75
-
76
- tx_result = self.acp_token.create_job(
77
- provider_address=provider_address,
78
- evaluator_address=evaluator_address,
79
- expire_at=expire_at
87
+ def create_job(
88
+ self,
89
+ provider_address: str,
90
+ price: float,
91
+ job_description: str,
92
+ evaluator_address: str,
93
+ expired_at: datetime,
94
+ ) -> int:
95
+ tx_result = self.acp_token.create_job(
96
+ provider_address = provider_address,
97
+ evaluator_address = evaluator_address,
98
+ expire_at = expired_at
80
99
  )
81
100
 
82
101
  job_id = None
@@ -128,7 +147,7 @@ class AcpClient:
128
147
  "providerAddress": provider_address,
129
148
  "description": job_description,
130
149
  "price": price,
131
- "expiredAt": expire_at.isoformat(),
150
+ "expiredAt": expired_at.astimezone(timezone.utc).isoformat(),
132
151
  "evaluatorAddress": evaluator_address
133
152
  }
134
153
 
@@ -230,7 +249,6 @@ class AcpClient:
230
249
  f"Response status code: {response.status_code}\n"
231
250
  f"Response description: {response.text}\n"
232
251
  )
233
- raise Exception(f"Failed to reset state: {response.status_code} {response.text}")
234
252
 
235
253
  def delete_completed_job(self, job_id: int) -> None:
236
254
  response = requests.delete(
@@ -3,8 +3,9 @@ import signal
3
3
  import sys
4
4
  from typing import List, Dict, Any, Optional,Tuple
5
5
  import json
6
- from dataclasses import dataclass
7
- from datetime import datetime
6
+ from dataclasses import dataclass, asdict
7
+ from datetime import datetime, timezone, timedelta
8
+ import traceback
8
9
 
9
10
  import socketio
10
11
  import socketio.client
@@ -26,6 +27,7 @@ class AcpPluginOptions:
26
27
  evaluator_cluster: Optional[str] = None
27
28
  on_evaluate: Optional[Callable[[IDeliverable], Tuple[bool, str]]] = None
28
29
  on_phase_change: Optional[Callable[[AcpJob], None]] = None
30
+ job_expiry_duration_mins: Optional[int] = None
29
31
 
30
32
 
31
33
  SocketEvents = {
@@ -74,6 +76,7 @@ class AcpPlugin:
74
76
  if options.on_phase_change is not None:
75
77
  self.on_phase_change = options.on_phase_change
76
78
  self.initializeSocket()
79
+ self.job_expiry_duration_mins = options.job_expiry_duration_mins if options.job_expiry_duration_mins is not None else 1440
77
80
 
78
81
 
79
82
 
@@ -152,8 +155,9 @@ class AcpPlugin:
152
155
 
153
156
  def get_acp_state(self) -> Dict:
154
157
  server_state = self.acp_client.get_state()
155
- server_state["inventory"]["produced"] = self.produced_inventory
156
- return server_state
158
+ server_state.inventory.produced = self.produced_inventory
159
+ state = asdict(server_state)
160
+ return state
157
161
 
158
162
  def get_worker(self, data: Optional[Dict] = None) -> WorkerConfig:
159
163
  functions = data.get("functions") if data else [
@@ -201,18 +205,42 @@ class AcpPlugin:
201
205
  def _search_agents_executable(self,reasoning: str, keyword: str) -> Tuple[FunctionResultStatus, str, dict]:
202
206
  if not reasoning:
203
207
  return FunctionResultStatus.FAILED, "Reasoning for the search must be provided. This helps track your decision-making process for future reference.", {}
204
-
205
- agents = self.acp_client.browse_agents(self.cluster, keyword)
206
-
208
+
209
+ agents = self.acp_client.browse_agents(self.cluster, keyword, rerank=True, top_k=1)
210
+
207
211
  if not agents:
208
212
  return FunctionResultStatus.FAILED, "No other trading agents found in the system. Please try again later when more agents are available.", {}
209
-
210
- return FunctionResultStatus.DONE, json.dumps({
211
- "availableAgents": [{"id": agent.id, "name": agent.name, "description": agent.description, "wallet_address": agent.wallet_address, "offerings": [{"name": offering.name, "price": offering.price} for offering in agent.offerings] if agent.offerings else []} for agent in agents],
212
- "totalAgentsFound": len(agents),
213
- "timestamp": datetime.now().timestamp(),
214
- "note": "Use the walletAddress when initiating a job with your chosen trading partner."
215
- }), {}
213
+
214
+ return (
215
+ FunctionResultStatus.DONE,
216
+ json.dumps(
217
+ {
218
+ "availableAgents": [
219
+ {
220
+ "id": agent.id,
221
+ "name": agent.name,
222
+ "description": agent.description,
223
+ "wallet_address": agent.wallet_address,
224
+ "offerings": (
225
+ [
226
+ {"name": offering.name, "price": offering.price}
227
+ for offering in agent.offerings
228
+ ]
229
+ if agent.offerings
230
+ else []
231
+ ),
232
+ "score": agent.score,
233
+ "explanation": agent.explanation
234
+ }
235
+ for agent in agents
236
+ ],
237
+ "totalAgentsFound": len(agents),
238
+ "timestamp": datetime.now().timestamp(),
239
+ "note": "Use the walletAddress when initiating a job with your chosen trading partner.",
240
+ }
241
+ ),
242
+ {},
243
+ )
216
244
 
217
245
  @property
218
246
  def search_agents_functions(self) -> Function:
@@ -290,7 +318,14 @@ class AcpPlugin:
290
318
  executable=self._initiate_job_executable
291
319
  )
292
320
 
293
- def _initiate_job_executable(self, sellerWalletAddress: str, price: str, reasoning: str, serviceRequirements: str, requireEvaluation: bool, evaluatorKeyword: str, tweetContent: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
321
+ def _initiate_job_executable(self, sellerWalletAddress: str, price: str, reasoning: str, serviceRequirements: str, requireEvaluation: str, evaluatorKeyword: str, tweetContent: Optional[str] = None) -> Tuple[FunctionResultStatus, str, dict]:
322
+ if isinstance(requireEvaluation, str):
323
+ require_evaluation = requireEvaluation.lower() == 'true'
324
+ elif isinstance(requireEvaluation, bool):
325
+ require_evaluation = requireEvaluation
326
+ else:
327
+ require_evaluation = False
328
+
294
329
  if not price:
295
330
  return FunctionResultStatus.FAILED, "Missing price - specify how much you're offering per unit", {}
296
331
 
@@ -306,13 +341,13 @@ class AcpPlugin:
306
341
  if not sellerWalletAddress:
307
342
  return FunctionResultStatus.FAILED, "Missing seller wallet address - specify the agent you want to buy from", {}
308
343
 
309
- if bool(requireEvaluation) and not evaluatorKeyword:
344
+ if require_evaluation and not evaluatorKeyword:
310
345
  return FunctionResultStatus.FAILED, "Missing validator keyword - provide a keyword to search for a validator", {}
311
346
 
312
347
  evaluatorAddress = self.acp_token_client.get_agent_wallet_address()
313
348
 
314
- if bool(requireEvaluation):
315
- validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluatorKeyword)
349
+ if require_evaluation:
350
+ validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluatorKeyword, rerank=True, top_k=1)
316
351
 
317
352
  if len(validators) == 0:
318
353
  return FunctionResultStatus.FAILED, "No evaluator found - try a different keyword", {}
@@ -320,11 +355,13 @@ class AcpPlugin:
320
355
  evaluatorAddress = validators[0].wallet_address
321
356
 
322
357
  # ... Rest of validation logic ...
358
+ expired_at = datetime.now(timezone.utc) + timedelta(minutes=self.job_expiry_duration_mins)
323
359
  job_id = self.acp_client.create_job(
324
360
  sellerWalletAddress,
325
361
  float(price),
326
362
  serviceRequirements,
327
- evaluatorAddress
363
+ evaluatorAddress,
364
+ expired_at
328
365
  )
329
366
 
330
367
  if (hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None and tweetContent is not None):
@@ -342,6 +379,7 @@ class AcpPlugin:
342
379
  "timestamp": datetime.now().timestamp(),
343
380
  }), {}
344
381
  except Exception as e:
382
+ print(traceback.format_exc())
345
383
  return FunctionResultStatus.FAILED, f"System error while initiating job - try again after a short delay. {str(e)}", {}
346
384
 
347
385
  @property
@@ -515,6 +553,7 @@ class AcpPlugin:
515
553
  "timestamp": datetime.now().timestamp()
516
554
  }), {}
517
555
  except Exception as e:
556
+ print(traceback.format_exc())
518
557
  return FunctionResultStatus.FAILED, f"System error while processing payment - try again after a short delay. {str(e)}", {}
519
558
 
520
559
  @property
@@ -585,7 +624,7 @@ class AcpPlugin:
585
624
  return FunctionResultStatus.FAILED, f"Cannot deliver - job is in '{job['phase']}' phase, must be in 'transaction' phase", {}
586
625
 
587
626
  produced = next(
588
- (i for i in self.produced_inventory if i["jobId"] == job["jobId"]),
627
+ (i for i in self.produced_inventory if i.jobId == job["jobId"]),
589
628
  None
590
629
  )
591
630
 
@@ -620,4 +659,5 @@ class AcpPlugin:
620
659
  "timestamp": datetime.now().timestamp()
621
660
  }), {}
622
661
  except Exception as e:
662
+ print(traceback.format_exc())
623
663
  return FunctionResultStatus.FAILED, f"System error while delivering items - try again after a short delay. {str(e)}", {}
@@ -8,6 +8,7 @@ from acp_plugin_gamesdk.acp_token_abi import ACP_TOKEN_ABI
8
8
  import requests
9
9
  from eth_account.messages import encode_defunct
10
10
  import json
11
+ import traceback
11
12
 
12
13
  class MemoType(IntEnum):
13
14
  MESSAGE = 0
@@ -97,6 +98,7 @@ class AcpToken:
97
98
  response = requests.post(f"{self.acp_base_url}/acp-agent-wallets/trx-result", json={"userOpHash": hash_value})
98
99
  return response.json()
99
100
  except Exception as error:
101
+ print(traceback.format_exc())
100
102
  raise Exception(f"Failed to get job_id {error}")
101
103
 
102
104
  def create_job(
@@ -134,8 +136,8 @@ class AcpToken:
134
136
  # Return transaction hash or response ID
135
137
  return {"txHash": response.json().get("data", {}).get("userOpHash", "")}
136
138
 
137
- except Exception as error:
138
- raise Exception(f"{error}")
139
+ except Exception as e:
140
+ raise
139
141
 
140
142
  def approve_allowance(self, price_in_wei: int) -> str:
141
143
  try:
@@ -158,8 +160,9 @@ class AcpToken:
158
160
  raise Exception(f"Failed to approve allowance {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
159
161
 
160
162
  return response.json()
161
- except Exception as error:
162
- raise Exception(f"{error}")
163
+ except Exception as e:
164
+ print(f"An error occurred while approving allowance: {e}")
165
+ raise
163
166
 
164
167
  def create_memo(
165
168
  self,
@@ -170,6 +173,7 @@ class AcpToken:
170
173
  next_phase: int
171
174
  ) -> dict:
172
175
  retries = 3
176
+ error = None
173
177
  while retries > 0:
174
178
  try:
175
179
  trx_data, signature = self._sign_transaction(
@@ -190,12 +194,15 @@ class AcpToken:
190
194
  raise Exception(f"Failed to create memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
191
195
 
192
196
  return { "txHash": response.json().get("txHash", response.json().get("id", "")), "memoId": response.json().get("memoId", "")}
193
- except Exception as error:
194
- print(f"{error}")
197
+ except Exception as e:
198
+ print(f"{e}")
199
+ print(traceback.format_exc())
200
+ error = e
195
201
  retries -= 1
196
202
  time.sleep(2 * (3 - retries))
197
203
 
198
- raise Exception(f"{error}")
204
+ if error:
205
+ raise Exception(f"{error}")
199
206
 
200
207
  def _sign_transaction(self, method_name: str, args: list, contract_address: Optional[str] = None) -> Tuple[dict, str]:
201
208
  if contract_address:
@@ -225,6 +232,7 @@ class AcpToken:
225
232
  reason: Optional[str] = ""
226
233
  ) -> str:
227
234
  retries = 3
235
+ error = None
228
236
  while retries > 0:
229
237
  try:
230
238
  trx_data, signature = self._sign_transaction(
@@ -246,8 +254,10 @@ class AcpToken:
246
254
 
247
255
  return response.json()
248
256
 
249
- except Exception as error:
257
+ except Exception as e:
258
+ error = e
250
259
  print(f"{error}")
260
+ print(traceback.format_exc())
251
261
  retries -= 1
252
262
  time.sleep(2 * (3 - retries))
253
263
 
@@ -1,11 +1,18 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import IntEnum, Enum
3
- from typing import List, Dict, Literal, Optional
3
+ from typing import List, Literal, Optional
4
4
 
5
5
  @dataclass
6
6
  class AcpOffering:
7
7
  name: str
8
8
  price: float
9
+
10
+ def __str__(self) -> str:
11
+ output = (
12
+ f"Offering(name={self.name}, price={self.price})"
13
+ )
14
+ return output
15
+
9
16
  @dataclass
10
17
  class AcpAgent:
11
18
  id: str
@@ -13,6 +20,23 @@ class AcpAgent:
13
20
  description: str
14
21
  wallet_address: str
15
22
  offerings: Optional[List[AcpOffering]]
23
+ score: Optional[float]
24
+ explanation: Optional[str]
25
+
26
+ def __str__(self) -> str:
27
+ offer = ""
28
+ if self.offerings:
29
+ for index, off in enumerate(self.offerings):
30
+ offer += f"{index+1}. {str(off)}\n"
31
+
32
+ output = (
33
+ f"😎 Agent ID={self.id}\n"
34
+ f"Name={self.name}, Description={self.description}, Wallet={self.wallet_address}\n"
35
+ f"Offerings:\n{offer}"
36
+ f"Score:\n{self.score}"
37
+ f"Explanation:\n{self.explanation}"
38
+ )
39
+ return output
16
40
 
17
41
  class AcpJobPhases(IntEnum):
18
42
  REQUEST = 0
@@ -33,7 +57,11 @@ class AcpJobPhasesDesc(str, Enum):
33
57
  @dataclass
34
58
  class AcpRequestMemo:
35
59
  id: int
36
- created_at: int
60
+ createdAt: int
61
+
62
+ def __repr__(self) -> str:
63
+ output = f"Memo(ID: {self.id}, created at: {self.createdAt})"
64
+ return output
37
65
 
38
66
  @dataclass
39
67
  class ITweet:
@@ -41,29 +69,59 @@ class ITweet:
41
69
  tweet_id: str
42
70
  content: str
43
71
  created_at: int
72
+
44
73
  @dataclass
45
74
  class AcpJob:
46
- job_id: int
75
+ jobId: Optional[int]
47
76
  desc: str
48
77
  price: str
49
78
  phase: AcpJobPhasesDesc
50
79
  memo: List[AcpRequestMemo]
51
- tweet_history : ITweet
52
- last_updated: int
80
+ lastUpdated: int
81
+ tweetHistory : ITweet | List
82
+ lastUpdated: int
83
+
84
+ def __repr__(self) -> str:
85
+ output =(
86
+ f"Job ID: {self.jobId}, "
87
+ f"Description: {self.desc}, "
88
+ f"Price: {self.price}, "
89
+ f"Phase: {self.phase.value}, "
90
+ f"Memo: {self.memo}, "
91
+ f"Last Updated: {self.lastUpdated})"
92
+ f"Tweet History: {self.tweetHistory}, "
93
+ f"Last Updated: {self.lastUpdated})"
94
+ )
95
+ return output
53
96
 
54
97
  @dataclass
55
98
  class IDeliverable:
56
- type: Literal["url", "text"]
99
+ type: str
57
100
  value: str
58
101
 
59
102
  @dataclass
60
103
  class IInventory(IDeliverable):
61
- job_id: int
104
+ jobId: int
62
105
 
63
106
  @dataclass
64
107
  class AcpJobsSection:
65
- as_a_buyer: List[AcpJob]
66
- as_a_seller: List[AcpJob]
108
+ asABuyer: List[AcpJob]
109
+ asASeller: List[AcpJob]
110
+
111
+ def __str__(self) -> str:
112
+ buyer_jobs = ""
113
+ for index, job in enumerate(self.asABuyer):
114
+ buyer_jobs += f"#{index+1} {str(job)} \n"
115
+
116
+ seller_jobs = ""
117
+ for index, job in enumerate(self.asASeller):
118
+ seller_jobs += f"#{index+1} {str(job)} \n"
119
+
120
+ output = (
121
+ f"As Buyer:\n{buyer_jobs}\n"
122
+ f"As Seller:\n{seller_jobs}\n"
123
+ )
124
+ return output
67
125
 
68
126
  @dataclass
69
127
  class AcpJobs:
@@ -71,12 +129,38 @@ class AcpJobs:
71
129
  completed: List[AcpJob]
72
130
  cancelled: List[AcpJob]
73
131
 
132
+ def __str__(self) -> str:
133
+ output = (
134
+ f"💻 Jobs\n"
135
+ f"🌕 Active Jobs:\n{self.active}\n"
136
+ f"🟢 Completed:\n{self.completed}\n"
137
+ f"🔴 Cancelled:\n{self.cancelled}\n"
138
+ )
139
+ return output
140
+
74
141
  @dataclass
75
142
  class AcpInventory:
76
- aquired: List[IInventory]
77
- produced: List[IInventory]
143
+ acquired: List[IInventory]
144
+ produced: Optional[List[IInventory]]
145
+
146
+ def __str__(self) -> str:
147
+ output = (
148
+ f"💼 Inventory\n"
149
+ f"Acquired: {self.acquired}\n"
150
+ f"Produced: {self.produced}\n"
151
+ )
152
+ return output
78
153
 
79
154
  @dataclass
80
155
  class AcpState:
81
156
  inventory: AcpInventory
82
157
  jobs: AcpJobs
158
+
159
+ def __str__(self) -> str:
160
+ output = (
161
+ f"🤖 Agent State".center(50, '=') + "\n" + \
162
+ f"{str(self.inventory)}\n" + \
163
+ f"{str(self.jobs)}\n" + \
164
+ f"State End".center(50, '=') + "\n"
165
+ )
166
+ return output
@@ -1,23 +1,26 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: acp-plugin-gamesdk
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: ACP Plugin for Python SDK for GAME by Virtuals
5
5
  Author: Steven Lee Soon Fatt
6
6
  Author-email: steven@virtuals.io
7
- Requires-Python: >=3.10,<4
7
+ Requires-Python: >=3.9,<3.13
8
8
  Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
9
10
  Classifier: Programming Language :: Python :: 3.10
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Programming Language :: Python :: 3.12
12
- Classifier: Programming Language :: Python :: 3.13
13
13
  Requires-Dist: aiohttp (>=3.11.14,<4.0.0)
14
+ Requires-Dist: dacite (>=1.9.2,<2.0.0)
14
15
  Requires-Dist: eth-account (>=0.13.6,<0.14.0)
15
16
  Requires-Dist: eth-typing (>=5.2.0,<6.0.0)
16
17
  Requires-Dist: eth-utils (>=5.2.0,<6.0.0)
17
18
  Requires-Dist: game-sdk (>=0.1.5)
18
19
  Requires-Dist: pydantic (>=2.10.6,<3.0.0)
20
+ Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
19
21
  Requires-Dist: python-socketio (>=5.11.1,<6.0.0)
20
22
  Requires-Dist: requests (>=2.32.3,<3.0.0)
23
+ Requires-Dist: rich (>=14.0.0,<15.0.0)
21
24
  Requires-Dist: twitter-plugin-gamesdk (>=0.2.2)
22
25
  Requires-Dist: virtuals-sdk (>=0.1.6,<0.2.0)
23
26
  Requires-Dist: web3 (>=7.9.0,<8.0.0)
@@ -164,6 +167,20 @@ acp_plugin = AcpPlugin(
164
167
 
165
168
  5. (Optional) If you want to listen to the `ON_EVALUATE` event, you can implement the `on_evaluate` function.
166
169
 
170
+
171
+ Evaluation refers to the process where buyer agent reviews the result submitted by the seller and decides whether to accept or reject it.
172
+ This is where the `on_evaluate` function comes into play. It allows your agent to programmatically verify deliverables and enforce quality checks.
173
+
174
+ 🔍 **Example implementations can be found in:**
175
+
176
+ Use Cases:
177
+ - Basic always-accept evaluation
178
+ - URL and file validation examples
179
+
180
+ Source Files:
181
+ - [examples/agentic/README.md](examples/agentic/README.md)
182
+ - [examples/reactive/README.md](examples/reactive/README.md)
183
+
167
184
  ```python
168
185
  def on_evaluate(deliverable: IDeliverable) -> Tuple[bool, str]:
169
186
  print(f"Evaluating deliverable: {deliverable}")
@@ -0,0 +1,8 @@
1
+ acp_plugin_gamesdk/acp_client.py,sha256=zF-FLFJ2PbS3LeI3cDx63pJyWCAVb8YQn7yZHQQ9wtY,9254
2
+ acp_plugin_gamesdk/acp_plugin.py,sha256=pFQ9QzGvs8EQ8kAkC_VAqc2tSTu9knueOBcK51oy9rk,28262
3
+ acp_plugin_gamesdk/acp_token.py,sha256=E7nkLVfXSzMozFoVcYGzVQyocnKQpohb_YlByZtVY_8,12078
4
+ acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
+ acp_plugin_gamesdk/interface.py,sha256=TsNQ74qW80YcUESOmBWX0wWzCTXG7S9eSF1mnfoJj0U,3983
6
+ acp_plugin_gamesdk-0.1.8.dist-info/METADATA,sha256=hh41VpuRlSke7KOjtp3Tbv9rlX29q7VNIIc0_Gh19eU,12223
7
+ acp_plugin_gamesdk-0.1.8.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
8
+ acp_plugin_gamesdk-0.1.8.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- acp_plugin_gamesdk/acp_client.py,sha256=JRrlfaRDgP2uZpbX8Vea-uHO6yicn488bmi_IXGVvt8,8896
2
- acp_plugin_gamesdk/acp_plugin.py,sha256=M4pZ6UyTk60CEA_R2tvKVRezd4CZQOFna94jzrM6_ss,26778
3
- acp_plugin_gamesdk/acp_token.py,sha256=Wx87e2Wd-5QUKoMWX_wf3IBEP-cl1pSyA9IZr1yYIDE,11796
4
- acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
- acp_plugin_gamesdk/interface.py,sha256=mWZ41SycGrNQbnC_IfBq8K6TgpguCvvbVTqFg8Uxy58,1549
6
- acp_plugin_gamesdk-0.1.6.dist-info/METADATA,sha256=w_mr3J8GkBN5AY6IU-CVfKpZG-lv4pu4cMSLOHxN7zw,11545
7
- acp_plugin_gamesdk-0.1.6.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
8
- acp_plugin_gamesdk-0.1.6.dist-info/RECORD,,