acp-plugin-gamesdk 0.1.4__py3-none-any.whl → 0.1.6__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.
- acp_plugin_gamesdk/acp_client.py +41 -4
- acp_plugin_gamesdk/acp_plugin.py +39 -15
- acp_plugin_gamesdk/acp_token.py +1 -0
- acp_plugin_gamesdk/interface.py +8 -1
- {acp_plugin_gamesdk-0.1.4.dist-info → acp_plugin_gamesdk-0.1.6.dist-info}/METADATA +5 -4
- acp_plugin_gamesdk-0.1.6.dist-info/RECORD +8 -0
- acp_plugin_gamesdk-0.1.4.dist-info/RECORD +0 -8
- {acp_plugin_gamesdk-0.1.4.dist-info → acp_plugin_gamesdk-0.1.6.dist-info}/WHEEL +0 -0
acp_plugin_gamesdk/acp_client.py
CHANGED
@@ -5,6 +5,7 @@ import requests
|
|
5
5
|
from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpOffering, AcpState
|
6
6
|
from acp_plugin_gamesdk.acp_token import AcpToken, MemoType
|
7
7
|
import time
|
8
|
+
import traceback
|
8
9
|
|
9
10
|
|
10
11
|
class AcpClient:
|
@@ -40,7 +41,12 @@ class AcpClient:
|
|
40
41
|
response = requests.get(url)
|
41
42
|
|
42
43
|
if response.status_code != 200:
|
43
|
-
raise Exception(
|
44
|
+
raise Exception(
|
45
|
+
f"Error occured in browse_agents function. Failed to browse agents.\n"
|
46
|
+
f"Response status code: {response.status_code}\n"
|
47
|
+
f"Response description: {response.text}\n"
|
48
|
+
)
|
49
|
+
|
44
50
|
|
45
51
|
response_json = response.json()
|
46
52
|
|
@@ -98,7 +104,8 @@ class AcpClient:
|
|
98
104
|
break
|
99
105
|
|
100
106
|
except Exception as e:
|
101
|
-
print(f"Error
|
107
|
+
print(f"Error in create_job function: {e}")
|
108
|
+
print(traceback.format_exc())
|
102
109
|
if attempt < retry_count - 1:
|
103
110
|
time.sleep(retry_delay)
|
104
111
|
else:
|
@@ -166,7 +173,15 @@ class AcpClient:
|
|
166
173
|
time.sleep(5)
|
167
174
|
self.acp_token.approve_allowance(amount_wei)
|
168
175
|
time.sleep(5)
|
169
|
-
|
176
|
+
self.acp_token.sign_memo(memo_id, True, reason)
|
177
|
+
time.sleep(5)
|
178
|
+
return self.acp_token.create_memo(
|
179
|
+
job_id=job_id,
|
180
|
+
content=f"Payment of {amount} made {reason}",
|
181
|
+
memo_type=MemoType.MESSAGE,
|
182
|
+
is_secured=False,
|
183
|
+
next_phase=AcpJobPhases.EVALUATION
|
184
|
+
)
|
170
185
|
|
171
186
|
def deliver_job(self, job_id: int, deliverable: str):
|
172
187
|
return self.acp_token.create_memo(
|
@@ -194,7 +209,11 @@ class AcpClient:
|
|
194
209
|
)
|
195
210
|
|
196
211
|
if response.status_code != 200 and response.status_code != 201:
|
197
|
-
raise Exception(
|
212
|
+
raise Exception(
|
213
|
+
f"Error occured in add_tweet function. Failed to add tweet.\n"
|
214
|
+
f"Response status code: {response.status_code}\n"
|
215
|
+
f"Response description: {response.text}\n"
|
216
|
+
)
|
198
217
|
|
199
218
|
|
200
219
|
return response.json()
|
@@ -206,4 +225,22 @@ class AcpClient:
|
|
206
225
|
)
|
207
226
|
|
208
227
|
if response.status_code not in [200, 204]:
|
228
|
+
raise Exception(
|
229
|
+
f"Error occured in reset_state function. Failed to reset state\n"
|
230
|
+
f"Response status code: {response.status_code}\n"
|
231
|
+
f"Response description: {response.text}\n"
|
232
|
+
)
|
209
233
|
raise Exception(f"Failed to reset state: {response.status_code} {response.text}")
|
234
|
+
|
235
|
+
def delete_completed_job(self, job_id: int) -> None:
|
236
|
+
response = requests.delete(
|
237
|
+
f"{self.base_url}/{job_id}/wallet/{self.agent_wallet_address}",
|
238
|
+
headers={"x-api-key": self.api_key}
|
239
|
+
)
|
240
|
+
|
241
|
+
if response.status_code not in [200, 204]:
|
242
|
+
raise Exception(
|
243
|
+
f"Error occurred in delete_completed_job function. Failed to delete job.\n"
|
244
|
+
f"Response status code: {response.status_code}\n"
|
245
|
+
f"Response description: {response.text}\n"
|
246
|
+
)
|
acp_plugin_gamesdk/acp_plugin.py
CHANGED
@@ -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:
|
@@ -57,22 +60,29 @@ class AcpPlugin:
|
|
57
60
|
"""
|
58
61
|
self.cluster = options.cluster
|
59
62
|
self.evaluator_cluster = options.evaluator_cluster
|
60
|
-
self.twitter_plugin =
|
63
|
+
self.twitter_plugin = None
|
64
|
+
if (options.twitter_plugin is not None):
|
65
|
+
self.twitter_plugin = options.twitter_plugin
|
66
|
+
|
61
67
|
self.produced_inventory: List[IInventory] = []
|
62
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"
|
63
|
-
if
|
69
|
+
if options.on_evaluate is not None or options.on_phase_change is not None:
|
64
70
|
print("Initializing socket")
|
65
|
-
self.on_evaluate = options.on_evaluate
|
66
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
|
67
76
|
self.initializeSocket()
|
68
77
|
|
78
|
+
|
79
|
+
|
69
80
|
def initializeSocket(self) -> Tuple[bool, str]:
|
70
81
|
"""
|
71
82
|
Initialize socket connection for real-time communication.
|
72
83
|
Returns a tuple of (success, message).
|
73
84
|
"""
|
74
85
|
try:
|
75
|
-
print("Initializing socket after")
|
76
86
|
self.socket = socketio.Client()
|
77
87
|
|
78
88
|
# Set up authentication before connecting
|
@@ -97,15 +107,23 @@ class AcpPlugin:
|
|
97
107
|
is_approved, reasoning = self.on_evaluate(deliverable)
|
98
108
|
|
99
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)
|
100
117
|
|
101
118
|
# Set up cleanup function for graceful shutdown
|
102
119
|
def cleanup():
|
103
120
|
if self.socket:
|
104
121
|
print("Disconnecting socket")
|
105
|
-
|
106
122
|
import time
|
107
123
|
time.sleep(1)
|
108
124
|
self.socket.disconnect()
|
125
|
+
|
126
|
+
|
109
127
|
|
110
128
|
def signal_handler(sig, frame):
|
111
129
|
cleanup()
|
@@ -120,6 +138,8 @@ class AcpPlugin:
|
|
120
138
|
return False, f"Failed to initialize socket: {str(e)}"
|
121
139
|
|
122
140
|
|
141
|
+
def set_on_phase_change(self, on_phase_change: Callable[[AcpJob], None]) -> None:
|
142
|
+
self.on_phase_change = on_phase_change
|
123
143
|
|
124
144
|
def add_produce_item(self, item: IInventory) -> None:
|
125
145
|
self.produced_inventory.append(item)
|
@@ -127,6 +147,9 @@ class AcpPlugin:
|
|
127
147
|
def reset_state(self) -> None:
|
128
148
|
self.acp_client.reset_state()
|
129
149
|
|
150
|
+
def delete_completed_job(self, job_id: int) -> None:
|
151
|
+
self.acp_client.delete_completed_job(job_id)
|
152
|
+
|
130
153
|
def get_acp_state(self) -> Dict:
|
131
154
|
server_state = self.acp_client.get_state()
|
132
155
|
server_state["inventory"]["produced"] = self.produced_inventory
|
@@ -252,7 +275,7 @@ class AcpPlugin:
|
|
252
275
|
|
253
276
|
args = [seller_wallet_address_arg, price_arg, reasoning_arg, service_requirements_arg, require_evaluation_arg, evaluator_keyword_arg]
|
254
277
|
|
255
|
-
if self.twitter_plugin is not None:
|
278
|
+
if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
|
256
279
|
tweet_content_arg = Argument(
|
257
280
|
name="tweetContent",
|
258
281
|
type="string",
|
@@ -304,7 +327,7 @@ class AcpPlugin:
|
|
304
327
|
evaluatorAddress
|
305
328
|
)
|
306
329
|
|
307
|
-
if (self.twitter_plugin is not None and tweetContent is not None):
|
330
|
+
if (hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None and tweetContent is not None):
|
308
331
|
post_tweet_fn = self.twitter_plugin.get_function('post_tweet')
|
309
332
|
tweet_id = post_tweet_fn(tweetContent, None).get('data', {}).get('id')
|
310
333
|
if (tweet_id is not None):
|
@@ -343,7 +366,7 @@ class AcpPlugin:
|
|
343
366
|
|
344
367
|
args = [job_id_arg, decision_arg, reasoning_arg]
|
345
368
|
|
346
|
-
if self.twitter_plugin is not None:
|
369
|
+
if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
|
347
370
|
tweet_content_arg = Argument(
|
348
371
|
name="tweetContent",
|
349
372
|
type="string",
|
@@ -389,7 +412,7 @@ class AcpPlugin:
|
|
389
412
|
reasoning
|
390
413
|
)
|
391
414
|
|
392
|
-
if (self.twitter_plugin is not None and tweetContent is not None):
|
415
|
+
if (hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None and tweetContent is not None):
|
393
416
|
tweet_history = job.get("tweetHistory", [])
|
394
417
|
tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
|
395
418
|
if (tweet_id is not None):
|
@@ -429,7 +452,7 @@ class AcpPlugin:
|
|
429
452
|
|
430
453
|
args = [job_id_arg, amount_arg, reasoning_arg]
|
431
454
|
|
432
|
-
if self.twitter_plugin is not None:
|
455
|
+
if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
|
433
456
|
tweet_content_arg = Argument(
|
434
457
|
name="tweetContent",
|
435
458
|
type="string",
|
@@ -476,7 +499,7 @@ class AcpPlugin:
|
|
476
499
|
reasoning
|
477
500
|
)
|
478
501
|
|
479
|
-
if (self.twitter_plugin is not None and tweetContent is not None):
|
502
|
+
if (hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None and tweetContent is not None):
|
480
503
|
tweet_history = job.get("tweetHistory", [])
|
481
504
|
tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
|
482
505
|
if (tweet_id is not None):
|
@@ -522,7 +545,7 @@ class AcpPlugin:
|
|
522
545
|
|
523
546
|
args = [job_id_arg, deliverable_type_arg, deliverable_arg, reasoning_arg]
|
524
547
|
|
525
|
-
if self.twitter_plugin is not None:
|
548
|
+
if hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None:
|
526
549
|
tweet_content_arg = Argument(
|
527
550
|
name="tweetContent",
|
528
551
|
type="string",
|
@@ -579,7 +602,8 @@ class AcpPlugin:
|
|
579
602
|
json.dumps(deliverable),
|
580
603
|
)
|
581
604
|
|
582
|
-
if (self.twitter_plugin is not None and tweetContent is not None):
|
605
|
+
if (hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None and tweetContent is not None):
|
606
|
+
|
583
607
|
tweet_history = job.get("tweetHistory", [])
|
584
608
|
tweet_id = tweet_history[-1].get("tweetId") if tweet_history else None
|
585
609
|
if (tweet_id is not None):
|
acp_plugin_gamesdk/acp_token.py
CHANGED
@@ -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
|
acp_plugin_gamesdk/interface.py
CHANGED
@@ -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.
|
3
|
+
Version: 0.1.6
|
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
|
@@ -134,7 +134,7 @@ acp_plugin = AcpPlugin(
|
|
134
134
|
> - This is where you can get your session entity key ID:
|
135
135
|
> 
|
136
136
|
|
137
|
-
4. (
|
137
|
+
4. (Optional) If you want to use GAME's twitter client with the ACP plugin, you can initialize it by running:
|
138
138
|
|
139
139
|
```python
|
140
140
|
twitter_client_options = {
|
@@ -200,7 +200,7 @@ agent = Agent(
|
|
200
200
|
)
|
201
201
|
```
|
202
202
|
|
203
|
-
|
203
|
+
7. Buyer-specific configurations
|
204
204
|
|
205
205
|
- <i>[Setting buyer agent goal]</i> Define what item needs to be "bought" and which worker to go to look for the item, e.g.
|
206
206
|
|
@@ -208,7 +208,7 @@ agent = Agent(
|
|
208
208
|
agent_goal = "You are an agent that gains market traction by posting memes. Your interest are in cats and AI. You can head to acp to look for agents to help you generate memes."
|
209
209
|
```
|
210
210
|
|
211
|
-
|
211
|
+
8. Seller-specific configurations
|
212
212
|
|
213
213
|
- <i>[Setting seller agent goal]</i> Define what item needs to be "sold" and which worker to go to respond to jobs, e.g.
|
214
214
|
|
@@ -288,4 +288,5 @@ To register your agent, please head over to the [agent registry](https://acp-sta
|
|
288
288
|
1. [Agent Commerce Protocol (ACP) research page](https://app.virtuals.io/research/agent-commerce-protocol)
|
289
289
|
- 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
290
|
- It includes the links to the multi-agent demo dashboard and paper.
|
291
|
+
2. [ACP Plugin FAQs](https://virtualsprotocol.notion.site/ACP-Plugin-FAQs-Troubleshooting-Tips-1d62d2a429e980eb9e61de851b6a7d60?pvs=4)
|
291
292
|
|
@@ -0,0 +1,8 @@
|
|
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,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
acp_plugin_gamesdk/acp_client.py,sha256=8wHyGL6VIWVTOkmXcYwDd1v_Tp4SqbWHpxXHc4fSl4c,7382
|
2
|
-
acp_plugin_gamesdk/acp_plugin.py,sha256=UZlC5SqVZw1F7Cg-oez9d7xwYfWJmcnMLDp6SA-QbMs,25459
|
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.4.dist-info/METADATA,sha256=iICvWAXJEdNAlbVW06cLm4En1zqUYKq5t0LGQ-K2SAc,11410
|
7
|
-
acp_plugin_gamesdk-0.1.4.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
8
|
-
acp_plugin_gamesdk-0.1.4.dist-info/RECORD,,
|
File without changes
|