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.
- acp_plugin_gamesdk/acp_client.py +45 -27
- acp_plugin_gamesdk/acp_plugin.py +60 -20
- acp_plugin_gamesdk/acp_token.py +18 -8
- acp_plugin_gamesdk/interface.py +95 -11
- {acp_plugin_gamesdk-0.1.6.dist-info → acp_plugin_gamesdk-0.1.8.dist-info}/METADATA +20 -3
- acp_plugin_gamesdk-0.1.8.dist-info/RECORD +8 -0
- acp_plugin_gamesdk-0.1.6.dist-info/RECORD +0 -8
- {acp_plugin_gamesdk-0.1.6.dist-info → acp_plugin_gamesdk-0.1.8.dist-info}/WHEEL +0 -0
acp_plugin_gamesdk/acp_client.py
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
-
|
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
|
-
|
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(
|
31
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
evaluator_address
|
79
|
-
|
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":
|
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(
|
acp_plugin_gamesdk/acp_plugin.py
CHANGED
@@ -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
|
156
|
-
|
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
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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:
|
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
|
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
|
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
|
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)}", {}
|
acp_plugin_gamesdk/acp_token.py
CHANGED
@@ -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
|
138
|
-
raise
|
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
|
162
|
-
|
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
|
194
|
-
print(f"{
|
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
|
-
|
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
|
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
|
|
acp_plugin_gamesdk/interface.py
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from enum import IntEnum, Enum
|
3
|
-
from typing import List,
|
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
|
-
|
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
|
-
|
75
|
+
jobId: Optional[int]
|
47
76
|
desc: str
|
48
77
|
price: str
|
49
78
|
phase: AcpJobPhasesDesc
|
50
79
|
memo: List[AcpRequestMemo]
|
51
|
-
|
52
|
-
|
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:
|
99
|
+
type: str
|
57
100
|
value: str
|
58
101
|
|
59
102
|
@dataclass
|
60
103
|
class IInventory(IDeliverable):
|
61
|
-
|
104
|
+
jobId: int
|
62
105
|
|
63
106
|
@dataclass
|
64
107
|
class AcpJobsSection:
|
65
|
-
|
66
|
-
|
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
|
-
|
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.
|
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.
|
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,,
|
File without changes
|