Skip to content

Commit 599432d

Browse files
committed
feat: add polling mode examples
1 parent 6c31122 commit 599432d

File tree

5 files changed

+148
-184
lines changed

5 files changed

+148
-184
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
WHITELISTED_WALLET_PRIVATE_KEY=<whitelisted-wallet-private-key>
2+
BUYER_AGENT_WALLET_ADDRESS=<buyer-agent-wallet-address>
3+
BUYER_ENTITY_ID=<buyer-entity-id>
4+
5+
SELLER_AGENT_WALLET_ADDRESS=<seller-agent-wallet-address>
6+
SELLER_ENTITY_ID=<seller-entity-id>
7+
8+
EVALUATOR_AGENT_WALLET_ADDRESS=<evaluator-agent-wallet-address>
9+
EVALUATOR_ENTITY_ID=<evaluator-entity-id>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import time
2+
from datetime import datetime, timedelta, timezone
3+
from dotenv import load_dotenv
4+
5+
from virtuals_acp import VirtualsACP, ACPJob, ACPJobPhase
6+
from virtuals_acp.env import EnvSettings
7+
8+
load_dotenv(override=True)
9+
10+
11+
# --- Configuration for the job polling interval ---
12+
POLL_INTERVAL_SECONDS = 20
13+
# --------------------------------------------------
14+
15+
16+
def buyer():
17+
env = EnvSettings()
18+
acp = VirtualsACP(
19+
wallet_private_key=env.WHITELISTED_WALLET_PRIVATE_KEY,
20+
agent_wallet_address=env.BUYER_AGENT_WALLET_ADDRESS,
21+
entity_id=env.BUYER_ENTITY_ID,
22+
)
23+
print(f"Buyer ACP Initialized. Agent: {acp.agent_address}")
24+
25+
# Browse available agents based on a keyword and cluster name
26+
relevant_agents = acp.browse_agents(
27+
keyword="Molly",
28+
cluster="yang-mainnet-test",
29+
graduated=False,
30+
)
31+
print(f"Relevant agents: {relevant_agents}")
32+
33+
# Pick one of the agents based on your criteria (in this example we just pick the first one)
34+
chosen_agent = relevant_agents[0]
35+
36+
# Pick one of the service offerings based on your criteria (in this example we just pick the first one)
37+
chosen_job_offering = chosen_agent.offerings[0]
38+
39+
# 1. Initiate Job
40+
print(f"\nInitiating job with Seller: {chosen_agent.wallet_address}, Evaluator: {env.EVALUATOR_AGENT_WALLET_ADDRESS}")
41+
42+
job_id = chosen_job_offering.initiate_job(
43+
# <your_schema_field> can be found in your ACP Visualiser's "Edit Service" pop-up.
44+
# Reference: (./images/specify_requirement_toggle_switch.png)
45+
service_requirement={"<your_schema_field>": "Help me to generate a flower meme."},
46+
evaluator_address=env.EVALUATOR_AGENT_WALLET_ADDRESS,
47+
expired_at=datetime.now() + timedelta(days=1),
48+
)
49+
50+
print(f"Job {job_id} initiated")
51+
# 2. Wait for Seller's acceptance memo (which sets next_phase to TRANSACTION)
52+
print(f"\nWaiting for Seller to accept job {job_id}...")
53+
54+
while True:
55+
# wait for some time before checking job again
56+
time.sleep(POLL_INTERVAL_SECONDS)
57+
job: ACPJob = acp.get_job_by_onchain_id(job_id)
58+
print(f"Polling Job {job_id}: Current Phase: {job.phase.name}")
59+
60+
if job.phase == ACPJobPhase.NEGOTIATION:
61+
# Check if there's a memo that indicates next phase is TRANSACTION
62+
for memo in job.memos:
63+
if memo.next_phase == ACPJobPhase.TRANSACTION:
64+
print("Paying job", job_id)
65+
job.pay(job.price)
66+
elif job.phase == ACPJobPhase.REQUEST:
67+
print(f"Job {job_id} still in REQUEST phase. Waiting for seller...")
68+
elif job.phase == ACPJobPhase.EVALUATION:
69+
print(f"Job {job_id} is in EVALUATION. Waiting for evaluator's decision...")
70+
elif job.phase == ACPJobPhase.TRANSACTION:
71+
print(f"Job {job_id} is in TRANSACTION. Waiting for seller to deliver...")
72+
elif job.phase == ACPJobPhase.COMPLETED:
73+
print("Job completed", job)
74+
break
75+
elif job.phase == ACPJobPhase.REJECTED:
76+
print("Job rejected", job)
77+
break
78+
79+
print("\n--- Buyer Script Finished ---")
80+
81+
if __name__ == "__main__":
82+
buyer()
Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,32 @@
1-
import os
21
import time
32
from typing import List
43
from dotenv import load_dotenv
54

65
from virtuals_acp import VirtualsACP, ACPJob, ACPJobPhase
7-
from virtuals_acp.configs import BASE_SEPOLIA_CONFIG
6+
from virtuals_acp.env import EnvSettings
87

98
load_dotenv(override=True)
109

11-
EVALUATOR_PRIVATE_KEY = os.environ.get("WHITELISTED_WALLET_PRIVATE_KEY")
12-
EVALUATOR_WALLET_ADDRESS = os.environ.get("EVALUATOR_AGENT_WALLET_ADDRESS")
1310

11+
# --- Configuration for the job polling interval ---
1412
POLL_INTERVAL_SECONDS = 20
13+
# --------------------------------------------------
1514

16-
if not all([EVALUATOR_PRIVATE_KEY, EVALUATOR_WALLET_ADDRESS]):
17-
print("Error: Ensure EVALUATOR_PRIVATE_KEY and EVALUATOR_WALLET_ADDRESS are set.")
18-
exit(1)
15+
def evaluator():
16+
env = EnvSettings()
1917

20-
def main():
21-
print("--- Evaluator Script (Simplified Direct Client Usage) ---")
22-
evaluator_acp = VirtualsACP(
23-
wallet_private_key=EVALUATOR_PRIVATE_KEY,
24-
agent_wallet_address=EVALUATOR_WALLET_ADDRESS,
25-
config=BASE_SEPOLIA_CONFIG
18+
acp = VirtualsACP(
19+
wallet_private_key=env.WHITELISTED_WALLET_PRIVATE_KEY,
20+
agent_wallet_address=env.EVALUATOR_AGENT_WALLET_ADDRESS,
21+
entity_id=env.EVALUATOR_ENTITY_ID,
2622
)
27-
print(f"Evaluator ACP Initialized. Agent: {evaluator_acp.agent_address}")
23+
print(f"Evaluator ACP Initialized. Agent: {acp.agent_address}")
2824

2925
evaluated_deliverables = set() # Store (job_id, deliverable_memo_id) to avoid re-evaluation
3026

3127
while True:
32-
print(f"\nEvaluator: Polling for jobs assigned to {EVALUATOR_WALLET_ADDRESS} requiring evaluation...")
33-
active_jobs_list: List[ACPJob] = evaluator_acp.get_active_jobs()
28+
print(f"\nEvaluator: Polling for jobs assigned to {acp.agent_address} requiring evaluation...")
29+
active_jobs_list: List[ACPJob] = acp.get_active_jobs()
3430

3531
if not active_jobs_list:
3632
print("Evaluator: No active jobs found in this poll.")
@@ -39,36 +35,40 @@ def main():
3935

4036
for job_summary in active_jobs_list:
4137
onchain_job_id = job_summary.id
42-
38+
4339
try:
44-
job_details = evaluator_acp.get_job_by_onchain_id(onchain_job_id)
45-
current_phase = job_details.phase
46-
40+
job = acp.get_job_by_onchain_id(onchain_job_id)
41+
current_phase = job.phase
42+
43+
# Ensure this job is for the current evaluator
44+
if job.evaluator_address != acp.agent_address:
45+
continue
46+
4747
if current_phase == ACPJobPhase.EVALUATION:
4848
print(f"Evaluator: Found Job {onchain_job_id} in EVALUATION phase.")
49-
49+
5050
# Find the seller's deliverable memo. Its next_phase should be COMPLETED.
5151
seller_deliverable_memo_to_sign = None
52-
for memo in reversed(job_details.memos): # Check latest first
52+
for memo in reversed(job.memos): # Check latest first
5353
if ACPJobPhase(memo.next_phase) == ACPJobPhase.COMPLETED:
5454
seller_deliverable_memo_to_sign = memo
5555
break
56-
56+
5757
if seller_deliverable_memo_to_sign:
5858
deliverable_key = (onchain_job_id, seller_deliverable_memo_to_sign.id)
5959
if deliverable_key in evaluated_deliverables:
60-
# print(f"Deliverable memo {seller_deliverable_memo_to_sign.id} for job {onchain_job_id} already processed.")
60+
print(f"Deliverable memo {seller_deliverable_memo_to_sign.id} for job {onchain_job_id} already processed.")
6161
continue
6262

6363
print(f" Job {onchain_job_id}: Found deliverable memo {seller_deliverable_memo_to_sign.id} to evaluate.")
6464
print(f" Deliverable Content: {seller_deliverable_memo_to_sign.content}")
65-
65+
6666
# Simple evaluation logic: always accept
6767
accept_the_delivery = True
6868
evaluation_reason = "Deliverable looks great, approved!"
69-
69+
7070
print(f" Job {onchain_job_id}: Evaluating... Accepting: {accept_the_delivery}")
71-
evaluator_acp.evaluate_job_delivery(
71+
acp.evaluate_job_delivery(
7272
memo_id_of_deliverable=seller_deliverable_memo_to_sign.id,
7373
accept=accept_the_delivery,
7474
reason=evaluation_reason
@@ -77,14 +77,16 @@ def main():
7777
evaluated_deliverables.add(deliverable_key)
7878
else:
7979
print(f" Job {onchain_job_id} in EVALUATION, but no deliverable memo (next_phase=COMPLETED) found yet.")
80+
elif current_phase in [ACPJobPhase.REQUEST, ACPJobPhase.NEGOTIATION]:
81+
print(f"Evaluator: Job {onchain_job_id} is in {current_phase.name} phase. Waiting for job to be delivered.")
82+
continue
8083
elif current_phase in [ACPJobPhase.COMPLETED, ACPJobPhase.REJECTED]:
8184
print(f"Evaluator: Job {onchain_job_id} is already in {current_phase.name}. No action.")
82-
# Potentially add to a "handled" set if not using evaluated_deliverables
83-
85+
8486
except Exception as e:
8587
print(f"Evaluator: Error processing job {onchain_job_id}: {e}")
86-
88+
8789
time.sleep(POLL_INTERVAL_SECONDS)
8890

8991
if __name__ == "__main__":
90-
main()
92+
evaluator()

examples/client_workflows/seller.py renamed to examples/acp_base/polling_mode/seller.py

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,53 @@
1-
import os
21
import time
32
import json
43
from typing import List
54
from dotenv import load_dotenv
65

76
from virtuals_acp import VirtualsACP, ACPJob, ACPJobPhase
8-
from virtuals_acp.configs import BASE_SEPOLIA_CONFIG
7+
from virtuals_acp.env import EnvSettings
98

109
load_dotenv(override=True)
1110

12-
SELLER_PRIVATE_KEY = os.environ.get("WHITELISTED_WALLET_PRIVATE_KEY")
13-
SELLER_WALLET_ADDRESS = os.environ.get("SELLER_AGENT_WALLET_ADDRESS")
14-
15-
if not all([SELLER_PRIVATE_KEY, SELLER_WALLET_ADDRESS]):
16-
print("Error: Ensure SELLER_PRIVATE_KEY and SELLER_WALLET_ADDRESS are set.")
17-
exit(1)
18-
19-
DELIVERABLE_CONTENT = {
20-
"type": "url",
21-
"value": "https://virtuals.io/delivered_meme.png",
22-
"notes": "Your meme is ready!"
23-
}
2411

12+
# --- Configuration for the job polling interval ---
2513
POLL_INTERVAL_SECONDS = 20
14+
# --------------------------------------------------
15+
16+
def seller():
17+
env = EnvSettings()
2618

27-
def main():
28-
print("--- Seller Script ---")
29-
seller_acp = VirtualsACP(
30-
wallet_private_key=SELLER_PRIVATE_KEY,
31-
agent_wallet_address=SELLER_WALLET_ADDRESS,
32-
config=BASE_SEPOLIA_CONFIG
19+
acp = VirtualsACP(
20+
wallet_private_key=env.WHITELISTED_WALLET_PRIVATE_KEY,
21+
agent_wallet_address=env.SELLER_AGENT_WALLET_ADDRESS,
22+
entity_id=env.SELLER_ENTITY_ID,
3323
)
34-
print(f"Seller ACP Initialized. Agent: {seller_acp.agent_address}")
24+
print(f"Seller ACP Initialized. Agent: {acp.agent_address}")
3525

3626
# Keep track of jobs to avoid reprocessing in this simple loop
3727
# job_id: {"responded_to_request": bool, "delivered_work": bool}
3828
processed_job_stages = {}
3929

4030
while True:
41-
print(f"\nSeller: Polling for active jobs for {SELLER_WALLET_ADDRESS}...")
42-
active_jobs_list: List[ACPJob] = seller_acp.get_active_jobs()
31+
print(f"\nSeller: Polling for active jobs for {env.SELLER_AGENT_WALLET_ADDRESS}...")
32+
active_jobs_list: List[ACPJob] = acp.get_active_jobs()
4333

4434
if not active_jobs_list:
4535
print("Seller: No active jobs found in this poll.")
4636
time.sleep(POLL_INTERVAL_SECONDS)
4737
continue
4838

49-
for job_summary in active_jobs_list:
50-
onchain_job_id = job_summary.id
39+
for job in active_jobs_list:
40+
onchain_job_id = job.id
5141

5242
# Ensure this job is for the current seller
53-
if job_summary.provider_address != seller_acp.agent_address:
43+
if job.provider_address != acp.agent_address:
5444
continue
5545

5646
job_stages = processed_job_stages.get(onchain_job_id, {})
5747

5848
try:
5949
# Fetch full details to get current phase and memos
60-
job_details = seller_acp.get_job_by_onchain_id(onchain_job_id)
50+
job_details = acp.get_job_by_onchain_id(onchain_job_id)
6151
current_phase = job_details.phase
6252
print(f"Seller: Checking job {onchain_job_id}. Current Phase: {current_phase.name}")
6353

@@ -72,7 +62,7 @@ def main():
7262

7363
if buyers_initial_memo_to_sign:
7464
print(f"Seller: Job {onchain_job_id} is in REQUEST. Responding to buyer's memo {buyers_initial_memo_to_sign.id}...")
75-
seller_acp.respond_to_job_memo(
65+
acp.respond_to_job_memo(
7666
job_id=onchain_job_id,
7767
memo_id=buyers_initial_memo_to_sign.id, # ID of the memo created by Buyer
7868
accept=True,
@@ -95,9 +85,12 @@ def main():
9585

9686
if buyers_payment_confirmation_memo:
9787
print(f"Seller: Job {onchain_job_id} is PAID (TRANSACTION phase). Submitting deliverable...")
98-
seller_acp.submit_job_deliverable(
88+
acp.submit_job_deliverable(
9989
job_id=onchain_job_id,
100-
deliverable_content=json.dumps(DELIVERABLE_CONTENT)
90+
deliverable_content=json.dumps({
91+
"type": "url",
92+
"value": "https://example.com"
93+
})
10194
)
10295
print(f"Seller: Deliverable submitted for job {onchain_job_id}. Job should move to EVALUATION.")
10396
job_stages["delivered_work"] = True
@@ -118,4 +111,4 @@ def main():
118111
time.sleep(POLL_INTERVAL_SECONDS)
119112

120113
if __name__ == "__main__":
121-
main()
114+
seller()

0 commit comments

Comments
 (0)