acp-plugin-gamesdk 0.1.5__tar.gz → 0.1.7__tar.gz
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.
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/PKG-INFO +16 -1
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/README.md +15 -0
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/acp_plugin_gamesdk/acp_client.py +28 -6
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/acp_plugin_gamesdk/acp_plugin.py +36 -8
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/acp_plugin_gamesdk/acp_token.py +1 -0
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/acp_plugin_gamesdk/interface.py +8 -1
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/pyproject.toml +1 -1
- {acp_plugin_gamesdk-0.1.5 → acp_plugin_gamesdk-0.1.7}/acp_plugin_gamesdk/acp_token_abi.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: acp-plugin-gamesdk
|
3
|
-
Version: 0.1.
|
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
|
|
@@ -138,6 +138,20 @@ acp_plugin = AcpPlugin(
|
|
138
138
|
|
139
139
|
5. (Optional) If you want to listen to the `ON_EVALUATE` event, you can implement the `on_evaluate` function.
|
140
140
|
|
141
|
+
|
142
|
+
Evaluation refers to the process where buyer agent reviews the result submitted by the seller and decides whether to accept or reject it.
|
143
|
+
This is where the `on_evaluate` function comes into play. It allows your agent to programmatically verify deliverables and enforce quality checks.
|
144
|
+
|
145
|
+
🔍 **Example implementations can be found in:**
|
146
|
+
|
147
|
+
Use Cases:
|
148
|
+
- Basic always-accept evaluation
|
149
|
+
- URL and file validation examples
|
150
|
+
|
151
|
+
Source Files:
|
152
|
+
- [examples/agentic/README.md](examples/agentic/README.md)
|
153
|
+
- [examples/reactive/README.md](examples/reactive/README.md)
|
154
|
+
|
141
155
|
```python
|
142
156
|
def on_evaluate(deliverable: IDeliverable) -> Tuple[bool, str]:
|
143
157
|
print(f"Evaluating deliverable: {deliverable}")
|
@@ -262,3 +276,4 @@ To register your agent, please head over to the [agent registry](https://acp-sta
|
|
262
276
|
1. [Agent Commerce Protocol (ACP) research page](https://app.virtuals.io/research/agent-commerce-protocol)
|
263
277
|
- This webpage introduces the Agent Commerce Protocol - A Standard for Permissionless AI Agent Commerce, a piece of research done by the Virtuals Protocol team
|
264
278
|
- It includes the links to the multi-agent demo dashboard and paper.
|
279
|
+
2. [ACP Plugin FAQs](https://virtualsprotocol.notion.site/ACP-Plugin-FAQs-Troubleshooting-Tips-1d62d2a429e980eb9e61de851b6a7d60?pvs=4)
|
@@ -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"
|
37
|
+
url += f"&search={requests.utils.quote(query)}"
|
35
38
|
|
36
39
|
if cluster:
|
37
|
-
|
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
|
-
|
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
|
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:
|
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
|
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
|
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
|
File without changes
|