acp-plugin-gamesdk 0.1.5__py3-none-any.whl → 0.1.7__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.
@@ -29,14 +29,15 @@ class AcpClient:
29
29
 
30
30
  def browse_agents(self, cluster: Optional[str] = None, query: Optional[str] = None) -> List[AcpAgent]:
31
31
  url = f"{self.acp_base_url}/agents"
32
-
32
+
33
+ # agent must exclude itself from search result to prevent self-commission
34
+ url += f"?filters[walletAddress][$notIn]={self.agent_wallet_address}"
35
+
33
36
  if query:
34
- url += f"?search={requests.utils.quote(query)}"
37
+ url += f"&search={requests.utils.quote(query)}"
35
38
 
36
39
  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)}"
40
+ url += f"&filters[cluster]={requests.utils.quote(cluster)}"
40
41
 
41
42
  response = requests.get(url)
42
43
 
@@ -173,7 +174,15 @@ class AcpClient:
173
174
  time.sleep(5)
174
175
  self.acp_token.approve_allowance(amount_wei)
175
176
  time.sleep(5)
176
- return self.acp_token.sign_memo(memo_id, True, reason)
177
+ self.acp_token.sign_memo(memo_id, True, reason)
178
+ time.sleep(5)
179
+ return self.acp_token.create_memo(
180
+ job_id=job_id,
181
+ content=f"Payment of {amount} made {reason}",
182
+ memo_type=MemoType.MESSAGE,
183
+ is_secured=False,
184
+ next_phase=AcpJobPhases.EVALUATION
185
+ )
177
186
 
178
187
  def deliver_job(self, job_id: int, deliverable: str):
179
188
  return self.acp_token.create_memo(
@@ -223,3 +232,16 @@ class AcpClient:
223
232
  f"Response description: {response.text}\n"
224
233
  )
225
234
  raise Exception(f"Failed to reset state: {response.status_code} {response.text}")
235
+
236
+ def delete_completed_job(self, job_id: int) -> None:
237
+ response = requests.delete(
238
+ f"{self.base_url}/{job_id}/wallet/{self.agent_wallet_address}",
239
+ headers={"x-api-key": self.api_key}
240
+ )
241
+
242
+ if response.status_code not in [200, 204]:
243
+ raise Exception(
244
+ f"Error occurred in delete_completed_job function. Failed to delete job.\n"
245
+ f"Response status code: {response.status_code}\n"
246
+ f"Response description: {response.text}\n"
247
+ )
@@ -15,7 +15,7 @@ from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
15
15
  from twitter_plugin_gamesdk.game_twitter_plugin import GameTwitterPlugin
16
16
  from acp_plugin_gamesdk.acp_client import AcpClient
17
17
  from acp_plugin_gamesdk.acp_token import AcpToken
18
- from acp_plugin_gamesdk.interface import AcpJobPhasesDesc, IDeliverable, IInventory
18
+ from acp_plugin_gamesdk.interface import AcpJobPhasesDesc, IDeliverable, IInventory, AcpJob
19
19
 
20
20
  @dataclass
21
21
  class AcpPluginOptions:
@@ -25,12 +25,15 @@ class AcpPluginOptions:
25
25
  cluster: Optional[str] = None
26
26
  evaluator_cluster: Optional[str] = None
27
27
  on_evaluate: Optional[Callable[[IDeliverable], Tuple[bool, str]]] = None
28
+ on_phase_change: Optional[Callable[[AcpJob], None]] = None
29
+
28
30
 
29
31
  SocketEvents = {
30
32
  "JOIN_EVALUATOR_ROOM": "joinEvaluatorRoom",
31
33
  "LEAVE_EVALUATOR_ROOM": "leaveEvaluatorRoom",
32
34
  "ON_EVALUATE": "onEvaluate",
33
- "ROOM_JOINED" : "roomJoined"
35
+ "ROOM_JOINED" : "roomJoined",
36
+ "ON_PHASE_CHANGE": "onPhaseChange"
34
37
  }
35
38
 
36
39
  class AcpPlugin:
@@ -63,12 +66,17 @@ class AcpPlugin:
63
66
 
64
67
  self.produced_inventory: List[IInventory] = []
65
68
  self.acp_base_url = self.acp_token_client.acp_base_url if self.acp_token_client.acp_base_url is None else "https://acpx-staging.virtuals.io/api"
66
- if (options.on_evaluate is not None):
69
+ if options.on_evaluate is not None or options.on_phase_change is not None:
67
70
  print("Initializing socket")
68
- self.on_evaluate = options.on_evaluate
69
71
  self.socket = None
72
+ if options.on_evaluate is not None:
73
+ self.on_evaluate = options.on_evaluate
74
+ if options.on_phase_change is not None:
75
+ self.on_phase_change = options.on_phase_change
70
76
  self.initializeSocket()
71
77
 
78
+
79
+
72
80
  def initializeSocket(self) -> Tuple[bool, str]:
73
81
  """
74
82
  Initialize socket connection for real-time communication.
@@ -99,15 +107,23 @@ class AcpPlugin:
99
107
  is_approved, reasoning = self.on_evaluate(deliverable)
100
108
 
101
109
  self.acp_token_client.sign_memo(memo_id, is_approved, reasoning)
110
+
111
+ # Set up event handler for phase changes
112
+ @self.socket.on(SocketEvents["ON_PHASE_CHANGE"])
113
+ def on_phase_change(data):
114
+ if hasattr(self, 'on_phase_change') and self.on_phase_change:
115
+ print(f"on_phase_change: {data}")
116
+ self.on_phase_change(data)
102
117
 
103
118
  # Set up cleanup function for graceful shutdown
104
119
  def cleanup():
105
120
  if self.socket:
106
121
  print("Disconnecting socket")
107
-
108
122
  import time
109
123
  time.sleep(1)
110
124
  self.socket.disconnect()
125
+
126
+
111
127
 
112
128
  def signal_handler(sig, frame):
113
129
  cleanup()
@@ -122,6 +138,8 @@ class AcpPlugin:
122
138
  return False, f"Failed to initialize socket: {str(e)}"
123
139
 
124
140
 
141
+ def set_on_phase_change(self, on_phase_change: Callable[[AcpJob], None]) -> None:
142
+ self.on_phase_change = on_phase_change
125
143
 
126
144
  def add_produce_item(self, item: IInventory) -> None:
127
145
  self.produced_inventory.append(item)
@@ -129,6 +147,9 @@ class AcpPlugin:
129
147
  def reset_state(self) -> None:
130
148
  self.acp_client.reset_state()
131
149
 
150
+ def delete_completed_job(self, job_id: int) -> None:
151
+ self.acp_client.delete_completed_job(job_id)
152
+
132
153
  def get_acp_state(self) -> Dict:
133
154
  server_state = self.acp_client.get_state()
134
155
  server_state["inventory"]["produced"] = self.produced_inventory
@@ -269,7 +290,14 @@ class AcpPlugin:
269
290
  executable=self._initiate_job_executable
270
291
  )
271
292
 
272
- 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]:
293
+ 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]:
294
+ if isinstance(requireEvaluation, str):
295
+ require_evaluation = requireEvaluation.lower() == 'true'
296
+ elif isinstance(requireEvaluation, bool):
297
+ require_evaluation = requireEvaluation
298
+ else:
299
+ require_evaluation = False
300
+
273
301
  if not price:
274
302
  return FunctionResultStatus.FAILED, "Missing price - specify how much you're offering per unit", {}
275
303
 
@@ -285,12 +313,12 @@ class AcpPlugin:
285
313
  if not sellerWalletAddress:
286
314
  return FunctionResultStatus.FAILED, "Missing seller wallet address - specify the agent you want to buy from", {}
287
315
 
288
- if bool(requireEvaluation) and not evaluatorKeyword:
316
+ if require_evaluation and not evaluatorKeyword:
289
317
  return FunctionResultStatus.FAILED, "Missing validator keyword - provide a keyword to search for a validator", {}
290
318
 
291
319
  evaluatorAddress = self.acp_token_client.get_agent_wallet_address()
292
320
 
293
- if bool(requireEvaluation):
321
+ if require_evaluation:
294
322
  validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluatorKeyword)
295
323
 
296
324
  if len(validators) == 0:
@@ -107,6 +107,7 @@ class AcpToken:
107
107
  ) -> dict:
108
108
  try:
109
109
  provider_address = Web3.to_checksum_address(provider_address)
110
+ evaluator_address = Web3.to_checksum_address(evaluator_address)
110
111
  expire_timestamp = int(expire_at.timestamp())
111
112
 
112
113
  # Sign the transaction
@@ -34,7 +34,13 @@ class AcpJobPhasesDesc(str, Enum):
34
34
  class AcpRequestMemo:
35
35
  id: int
36
36
  created_at: int
37
-
37
+
38
+ @dataclass
39
+ class ITweet:
40
+ type: Literal["buyer", "seller"]
41
+ tweet_id: str
42
+ content: str
43
+ created_at: int
38
44
  @dataclass
39
45
  class AcpJob:
40
46
  job_id: int
@@ -42,6 +48,7 @@ class AcpJob:
42
48
  price: str
43
49
  phase: AcpJobPhasesDesc
44
50
  memo: List[AcpRequestMemo]
51
+ tweet_history : ITweet
45
52
  last_updated: int
46
53
 
47
54
  @dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: acp-plugin-gamesdk
3
- Version: 0.1.5
3
+ Version: 0.1.7
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
@@ -164,6 +164,20 @@ acp_plugin = AcpPlugin(
164
164
 
165
165
  5. (Optional) If you want to listen to the `ON_EVALUATE` event, you can implement the `on_evaluate` function.
166
166
 
167
+
168
+ Evaluation refers to the process where buyer agent reviews the result submitted by the seller and decides whether to accept or reject it.
169
+ This is where the `on_evaluate` function comes into play. It allows your agent to programmatically verify deliverables and enforce quality checks.
170
+
171
+ 🔍 **Example implementations can be found in:**
172
+
173
+ Use Cases:
174
+ - Basic always-accept evaluation
175
+ - URL and file validation examples
176
+
177
+ Source Files:
178
+ - [examples/agentic/README.md](examples/agentic/README.md)
179
+ - [examples/reactive/README.md](examples/reactive/README.md)
180
+
167
181
  ```python
168
182
  def on_evaluate(deliverable: IDeliverable) -> Tuple[bool, str]:
169
183
  print(f"Evaluating deliverable: {deliverable}")
@@ -288,4 +302,5 @@ To register your agent, please head over to the [agent registry](https://acp-sta
288
302
  1. [Agent Commerce Protocol (ACP) research page](https://app.virtuals.io/research/agent-commerce-protocol)
289
303
  - This webpage introduces the Agent Commerce Protocol - A Standard for Permissionless AI Agent Commerce, a piece of research done by the Virtuals Protocol team
290
304
  - It includes the links to the multi-agent demo dashboard and paper.
305
+ 2. [ACP Plugin FAQs](https://virtualsprotocol.notion.site/ACP-Plugin-FAQs-Troubleshooting-Tips-1d62d2a429e980eb9e61de851b6a7d60?pvs=4)
291
306
 
@@ -0,0 +1,8 @@
1
+ acp_plugin_gamesdk/acp_client.py,sha256=juFDXMaRUAMhf1ullsDC0_krxPBNr80JDZ7eXiBVDls,8925
2
+ acp_plugin_gamesdk/acp_plugin.py,sha256=NkKjuEQZ5L3KD1GcuiUen8Q2nOewpj28p1IGoIwA5NI,27038
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.7.dist-info/METADATA,sha256=Z0qpNdTJG32s_Yl9zceDYWrkX08HGxqTYXBQde8ziMA,12098
7
+ acp_plugin_gamesdk-0.1.7.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
8
+ acp_plugin_gamesdk-0.1.7.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- acp_plugin_gamesdk/acp_client.py,sha256=Gs8Id-ecDwuJsm4agoHRp2dv_bKSBXDq0XGlHd99du8,8070
2
- acp_plugin_gamesdk/acp_plugin.py,sha256=ft6fTwmt9HVzFRXc1PVTdigcTVLjRfOeu9enNN2RSYQ,25802
3
- acp_plugin_gamesdk/acp_token.py,sha256=CL-0Ww1i0tzDNj0sAuY3DvlWIKrEGoMcb0Z69e5X9og,11720
4
- acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
5
- acp_plugin_gamesdk/interface.py,sha256=5jStptqzT_dTlO-d51Xp4PaOVYeFV6uss70GvpLfCEM,1401
6
- acp_plugin_gamesdk-0.1.5.dist-info/METADATA,sha256=2H8SH3wzvquhJTrquxqwh7kopWnmDw8fDmwYwBkFbGk,11410
7
- acp_plugin_gamesdk-0.1.5.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
8
- acp_plugin_gamesdk-0.1.5.dist-info/RECORD,,