|
| 1 | +import logging |
1 | 2 | import threading |
2 | 3 | from datetime import datetime, timedelta |
3 | 4 | from typing import Optional |
4 | 5 |
|
5 | 6 | from dotenv import load_dotenv |
6 | | - |
7 | | -from virtuals_acp import ACPMemo |
| 7 | +from virtuals_acp.memo import ACPMemo |
8 | 8 | from virtuals_acp.client import VirtualsACP |
9 | 9 | from virtuals_acp.env import EnvSettings |
10 | 10 | from virtuals_acp.job import ACPJob |
|
14 | 14 | ACPGraduationStatus, |
15 | 15 | ACPOnlineStatus, |
16 | 16 | ) |
| 17 | +from virtuals_acp.configs.configs import BASE_MAINNET_ACP_X402_CONFIG_V2 |
| 18 | +from virtuals_acp.contract_clients.contract_client_v2 import ACPContractClientV2 |
17 | 19 |
|
18 | | -load_dotenv(override=True) |
| 20 | +# Configure logging |
| 21 | +logging.basicConfig( |
| 22 | + level=logging.INFO, |
| 23 | + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", |
| 24 | +) |
| 25 | +logger = logging.getLogger("BuyerAgent") |
19 | 26 |
|
| 27 | +load_dotenv(override=True) |
20 | 28 |
|
21 | 29 | def buyer(): |
22 | 30 | env = EnvSettings() |
23 | 31 |
|
24 | 32 | def on_new_task(job: ACPJob, memo_to_sign: Optional[ACPMemo] = None): |
25 | | - print(f"[on_new_task] Received job {job.id} (phase: {job.phase})") |
| 33 | + logger.info(f"[on_new_task] Received job {job.id} (phase: {job.phase})") |
26 | 34 | if ( |
27 | 35 | job.phase == ACPJobPhase.NEGOTIATION |
28 | 36 | and memo_to_sign is not None |
29 | 37 | and memo_to_sign.next_phase == ACPJobPhase.TRANSACTION |
30 | 38 | ): |
31 | | - print("Paying job", job.id) |
32 | | - job.pay(job.price) |
| 39 | + logger.info(f"Paying job {job.id}") |
| 40 | + job.pay_and_accept_requirement() |
| 41 | + logger.info(f"Job {job.id} paid") |
| 42 | + elif ( |
| 43 | + job.phase == ACPJobPhase.TRANSACTION |
| 44 | + and memo_to_sign is not None |
| 45 | + and memo_to_sign.next_phase == ACPJobPhase.REJECTED |
| 46 | + ): |
| 47 | + logger.info(f"Signing job rejection memo {job}") |
| 48 | + memo_to_sign.sign(True, "accepts job rejection") |
| 49 | + logger.info(f"Job {job.id} rejection memo signed") |
33 | 50 | elif job.phase == ACPJobPhase.COMPLETED: |
34 | | - print("Job completed", job) |
| 51 | + logger.info(f"Job {job.id} completed") |
35 | 52 | elif job.phase == ACPJobPhase.REJECTED: |
36 | | - print("Job rejected", job) |
37 | | - |
38 | | - def on_evaluate(job: ACPJob): |
39 | | - print(f"Evaluation function called for job {job.id}") |
40 | | - job.evaluate(True) |
41 | | - |
42 | | - if env.WHITELISTED_WALLET_PRIVATE_KEY is None: |
43 | | - raise Exception("WHITELISTED_WALLET_PRIVATE_KEY is not set") |
44 | | - if env.BUYER_ENTITY_ID is None: |
45 | | - raise Exception("BUYER_ENTITY_ID is not set") |
46 | | - if env.BUYER_AGENT_WALLET_ADDRESS is None: |
47 | | - raise Exception("BUYER_AGENT_WALLET_ADDRESS is not set") |
48 | | - |
49 | | - acp = VirtualsACP( |
50 | | - wallet_private_key=env.WHITELISTED_WALLET_PRIVATE_KEY, |
51 | | - agent_wallet_address=env.BUYER_AGENT_WALLET_ADDRESS, |
52 | | - on_new_task=on_new_task, |
53 | | - on_evaluate=on_evaluate, |
54 | | - entity_id=env.BUYER_ENTITY_ID, |
| 53 | + logger.info(f"Job {job.id} rejected") |
| 54 | + |
| 55 | + acp_client = VirtualsACP( |
| 56 | + acp_contract_clients=ACPContractClientV2( |
| 57 | + wallet_private_key=env.WHITELISTED_WALLET_PRIVATE_KEY, |
| 58 | + agent_wallet_address=env.BUYER_AGENT_WALLET_ADDRESS, |
| 59 | + entity_id=env.BUYER_ENTITY_ID, |
| 60 | + config=BASE_MAINNET_ACP_X402_CONFIG_V2, # route to x402 for payment, undefined defaulted back to direct transfer |
| 61 | + ), |
| 62 | + on_new_task=on_new_task |
55 | 63 | ) |
56 | 64 |
|
57 | | - # Browse available agents based on a keyword and cluster name |
58 | | - relevant_agents = acp.browse_agents( |
59 | | - keyword="<your_filter_agent_keyword>", |
60 | | - cluster="<your_cluster_name>", |
61 | | - sort_by=[ |
62 | | - ACPAgentSort.SUCCESSFUL_JOB_COUNT, |
63 | | - ], |
| 65 | + # Browse available agents |
| 66 | + relevant_agents = acp_client.browse_agents( |
| 67 | + keyword="<your-filter-agent-keyword>", |
| 68 | + sort_by=[ACPAgentSort.SUCCESSFUL_JOB_COUNT], |
64 | 69 | top_k=5, |
65 | 70 | graduation_status=ACPGraduationStatus.ALL, |
66 | 71 | online_status=ACPOnlineStatus.ALL, |
| 72 | + show_hidden_offerings=True, |
67 | 73 | ) |
68 | | - print(f"Relevant agents: {relevant_agents}") |
| 74 | + logger.info(f"Relevant agents: {relevant_agents}") |
69 | 75 |
|
70 | | - # Pick one of the agents based on your criteria (in this example we just pick the first one) |
| 76 | + # Pick the first agent |
71 | 77 | chosen_agent = relevant_agents[0] |
72 | 78 |
|
73 | | - # Pick one of the service offerings based on your criteria (in this example we just pick the first one) |
74 | | - chosen_job_offering = chosen_agent.offerings[0] |
| 79 | + # Pick the first job offering |
| 80 | + chosen_job_offering = chosen_agent.job_offerings[0] |
75 | 81 |
|
| 82 | + # Initiate job with plain string requirement |
76 | 83 | job_id = chosen_job_offering.initiate_job( |
77 | | - # <your_schema_field> can be found in your ACP Visualiser's "Edit Service" pop-up. |
78 | | - # Reference: (./images/specify_requirement_toggle_switch.png) |
79 | 84 | service_requirement={ |
80 | | - "<your_schema_field>": "Help me to generate a flower meme." |
| 85 | + "<your-schema-key-1>": "<your-schema-value-1>", |
| 86 | + "<your-schema-key-2>": "<your-schema-value-2>", |
81 | 87 | }, |
82 | | - evaluator_address=env.EVALUATOR_AGENT_WALLET_ADDRESS, |
83 | | - expired_at=datetime.now() + timedelta(days=1), |
| 88 | + evaluator_address=env.EVALUATOR_AGENT_WALLET_ADDRESS, # evaluator address |
| 89 | + expired_at=datetime.now() + timedelta(minutes=3.1) # job expiry duration, minimum 3 minutes |
84 | 90 | ) |
85 | 91 |
|
86 | | - print(f"Job {job_id} initiated") |
87 | | - print("Listening for next steps...") |
88 | | - # Keep the script running to listen for next steps |
| 92 | + logger.info(f"Job {job_id} initiated") |
| 93 | + logger.info("Listening for next steps...") |
| 94 | + |
| 95 | + # Keep script alive |
89 | 96 | threading.Event().wait() |
90 | 97 |
|
91 | 98 |
|
|
0 commit comments