acp-plugin-gamesdk 0.1.7__tar.gz → 0.1.9__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.
@@ -1,23 +1,26 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: acp-plugin-gamesdk
3
- Version: 0.1.7
3
+ Version: 0.1.9
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.10,<4
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)
@@ -1,12 +1,16 @@
1
- from datetime import datetime, timedelta
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,22 +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
- return response.json()
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(self, cluster: Optional[str] = None, query: Optional[str] = None) -> List[AcpAgent]:
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]:
43
+
31
44
  url = f"{self.acp_base_url}/agents"
32
45
 
33
- # agent must exclude itself from search result to prevent self-commission
34
- url += f"?filters[walletAddress][$notIn]={self.agent_wallet_address}"
35
-
36
- if query:
37
- url += f"&search={requests.utils.quote(query)}"
38
-
39
- if cluster:
40
- url += f"&filters[cluster]={requests.utils.quote(cluster)}"
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)
41
54
 
42
- response = requests.get(url)
43
-
44
55
  if response.status_code != 200:
45
56
  raise Exception(
46
57
  f"Error occured in browse_agents function. Failed to browse agents.\n"
@@ -65,19 +76,26 @@ class AcpClient:
65
76
  name=agent["name"],
66
77
  description=agent["description"],
67
78
  wallet_address=agent["walletAddress"],
68
- offerings=offerings
79
+ offerings=offerings,
80
+ score=agent["score"],
81
+ explanation=agent["explanation"]
69
82
  )
70
83
  )
71
84
 
72
85
  return result
73
86
 
74
- def create_job(self, provider_address: str, price: float, job_description: str, evaluator_address: str) -> int:
75
- expire_at = datetime.now() + timedelta(days=1)
76
-
77
- tx_result = self.acp_token.create_job(
78
- provider_address=provider_address,
79
- evaluator_address=evaluator_address,
80
- expire_at=expire_at
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
81
99
  )
82
100
 
83
101
  job_id = None
@@ -129,7 +147,7 @@ class AcpClient:
129
147
  "providerAddress": provider_address,
130
148
  "description": job_description,
131
149
  "price": price,
132
- "expiredAt": expire_at.isoformat(),
150
+ "expiredAt": expired_at.astimezone(timezone.utc).isoformat(),
133
151
  "evaluatorAddress": evaluator_address
134
152
  }
135
153
 
@@ -231,7 +249,6 @@ class AcpClient:
231
249
  f"Response status code: {response.status_code}\n"
232
250
  f"Response description: {response.text}\n"
233
251
  )
234
- raise Exception(f"Failed to reset state: {response.status_code} {response.text}")
235
252
 
236
253
  def delete_completed_job(self, job_id: int) -> None:
237
254
  response = requests.delete(
@@ -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["inventory"]["produced"] = self.produced_inventory
156
- return server_state
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 FunctionResultStatus.DONE, json.dumps({
211
- "availableAgents": [{"id": agent.id, "name": agent.name, "description": agent.description, "wallet_address": agent.wallet_address, "offerings": [{"name": offering.name, "price": offering.price} for offering in agent.offerings] if agent.offerings else []} for agent in agents],
212
- "totalAgentsFound": len(agents),
213
- "timestamp": datetime.now().timestamp(),
214
- "note": "Use the walletAddress when initiating a job with your chosen trading partner."
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:
@@ -319,7 +347,7 @@ class AcpPlugin:
319
347
  evaluatorAddress = self.acp_token_client.get_agent_wallet_address()
320
348
 
321
349
  if require_evaluation:
322
- validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluatorKeyword)
350
+ validators = self.acp_client.browse_agents(self.evaluator_cluster, evaluatorKeyword, rerank=True, top_k=1)
323
351
 
324
352
  if len(validators) == 0:
325
353
  return FunctionResultStatus.FAILED, "No evaluator found - try a different keyword", {}
@@ -327,11 +355,13 @@ class AcpPlugin:
327
355
  evaluatorAddress = validators[0].wallet_address
328
356
 
329
357
  # ... Rest of validation logic ...
358
+ expired_at = datetime.now(timezone.utc) + timedelta(minutes=self.job_expiry_duration_mins)
330
359
  job_id = self.acp_client.create_job(
331
360
  sellerWalletAddress,
332
361
  float(price),
333
362
  serviceRequirements,
334
- evaluatorAddress
363
+ evaluatorAddress,
364
+ expired_at
335
365
  )
336
366
 
337
367
  if (hasattr(self, 'twitter_plugin') and self.twitter_plugin is not None and tweetContent is not None):
@@ -349,6 +379,7 @@ class AcpPlugin:
349
379
  "timestamp": datetime.now().timestamp(),
350
380
  }), {}
351
381
  except Exception as e:
382
+ print(traceback.format_exc())
352
383
  return FunctionResultStatus.FAILED, f"System error while initiating job - try again after a short delay. {str(e)}", {}
353
384
 
354
385
  @property
@@ -522,6 +553,7 @@ class AcpPlugin:
522
553
  "timestamp": datetime.now().timestamp()
523
554
  }), {}
524
555
  except Exception as e:
556
+ print(traceback.format_exc())
525
557
  return FunctionResultStatus.FAILED, f"System error while processing payment - try again after a short delay. {str(e)}", {}
526
558
 
527
559
  @property
@@ -592,7 +624,7 @@ class AcpPlugin:
592
624
  return FunctionResultStatus.FAILED, f"Cannot deliver - job is in '{job['phase']}' phase, must be in 'transaction' phase", {}
593
625
 
594
626
  produced = next(
595
- (i for i in self.produced_inventory if i["jobId"] == job["jobId"]),
627
+ (i for i in self.produced_inventory if (i["jobId"] if isinstance(i, dict) else i.jobId) == job["jobId"]),
596
628
  None
597
629
  )
598
630
 
@@ -627,4 +659,5 @@ class AcpPlugin:
627
659
  "timestamp": datetime.now().timestamp()
628
660
  }), {}
629
661
  except Exception as e:
662
+ print(traceback.format_exc())
630
663
  return FunctionResultStatus.FAILED, f"System error while delivering items - try again after a short delay. {str(e)}", {}
@@ -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 error:
138
- raise Exception(f"{error}")
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 error:
162
- raise Exception(f"{error}")
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 error:
194
- print(f"{error}")
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
- raise Exception(f"{error}")
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 error:
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
 
@@ -0,0 +1,166 @@
1
+ from dataclasses import dataclass
2
+ from enum import IntEnum, Enum
3
+ from typing import List, Literal, Optional
4
+
5
+ @dataclass
6
+ class AcpOffering:
7
+ name: str
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
+
16
+ @dataclass
17
+ class AcpAgent:
18
+ id: str
19
+ name: str
20
+ description: str
21
+ wallet_address: str
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
40
+
41
+ class AcpJobPhases(IntEnum):
42
+ REQUEST = 0
43
+ NEGOTIATION = 1
44
+ TRANSACTION = 2
45
+ EVALUATION = 3
46
+ COMPLETED = 4
47
+ REJECTED = 5
48
+
49
+ class AcpJobPhasesDesc(str, Enum):
50
+ REQUEST = "request"
51
+ NEGOTIATION = "pending_payment"
52
+ TRANSACTION = "in_progress"
53
+ EVALUATION = "evaluation"
54
+ COMPLETED = "completed"
55
+ REJECTED = "rejected"
56
+
57
+ @dataclass
58
+ class AcpRequestMemo:
59
+ id: int
60
+ createdAt: int
61
+
62
+ def __repr__(self) -> str:
63
+ output = f"Memo(ID: {self.id}, created at: {self.createdAt})"
64
+ return output
65
+
66
+ @dataclass
67
+ class ITweet:
68
+ type: Literal["buyer", "seller"]
69
+ tweet_id: str
70
+ content: str
71
+ created_at: int
72
+
73
+ @dataclass
74
+ class AcpJob:
75
+ jobId: Optional[int]
76
+ desc: str
77
+ price: str
78
+ phase: AcpJobPhasesDesc
79
+ memo: List[AcpRequestMemo]
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
96
+
97
+ @dataclass
98
+ class IDeliverable:
99
+ type: str
100
+ value: str
101
+
102
+ @dataclass
103
+ class IInventory(IDeliverable):
104
+ jobId: int
105
+
106
+ @dataclass
107
+ class AcpJobsSection:
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
125
+
126
+ @dataclass
127
+ class AcpJobs:
128
+ active: AcpJobsSection
129
+ completed: List[AcpJob]
130
+ cancelled: List[AcpJob]
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
+
141
+ @dataclass
142
+ class AcpInventory:
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
153
+
154
+ @dataclass
155
+ class AcpState:
156
+ inventory: AcpInventory
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,12 +1,12 @@
1
1
  [tool.poetry]
2
2
  name = "acp-plugin-gamesdk"
3
- version = "0.1.7"
3
+ version = "0.1.9"
4
4
  description = "ACP Plugin for Python SDK for GAME by Virtuals"
5
5
  authors = ["Steven Lee Soon Fatt <steven@virtuals.io>"]
6
6
  readme = "README.md"
7
7
 
8
8
  [tool.poetry.dependencies]
9
- python = ">=3.10,<4"
9
+ python = ">=3.9,<3.13"
10
10
  web3 = "^7.9.0"
11
11
  virtuals-sdk = "^0.1.6"
12
12
  aiohttp = "^3.11.14"
@@ -19,6 +19,9 @@ twitter-plugin-gamesdk = ">=0.2.2"
19
19
  game-sdk = ">=0.1.5"
20
20
  python-socketio = "^5.11.1"
21
21
  websocket-client = "^1.7.0"
22
+ python-dotenv = "^1.1.0"
23
+ dacite = "^1.9.2"
24
+ rich = ">=14.0.0,<15.0.0"
22
25
 
23
26
  [build-system]
24
27
  requires = ["poetry-core"]
@@ -1,82 +0,0 @@
1
- from dataclasses import dataclass
2
- from enum import IntEnum, Enum
3
- from typing import List, Dict, Literal, Optional
4
-
5
- @dataclass
6
- class AcpOffering:
7
- name: str
8
- price: float
9
- @dataclass
10
- class AcpAgent:
11
- id: str
12
- name: str
13
- description: str
14
- wallet_address: str
15
- offerings: Optional[List[AcpOffering]]
16
-
17
- class AcpJobPhases(IntEnum):
18
- REQUEST = 0
19
- NEGOTIATION = 1
20
- TRANSACTION = 2
21
- EVALUATION = 3
22
- COMPLETED = 4
23
- REJECTED = 5
24
-
25
- class AcpJobPhasesDesc(str, Enum):
26
- REQUEST = "request"
27
- NEGOTIATION = "pending_payment"
28
- TRANSACTION = "in_progress"
29
- EVALUATION = "evaluation"
30
- COMPLETED = "completed"
31
- REJECTED = "rejected"
32
-
33
- @dataclass
34
- class AcpRequestMemo:
35
- id: int
36
- created_at: int
37
-
38
- @dataclass
39
- class ITweet:
40
- type: Literal["buyer", "seller"]
41
- tweet_id: str
42
- content: str
43
- created_at: int
44
- @dataclass
45
- class AcpJob:
46
- job_id: int
47
- desc: str
48
- price: str
49
- phase: AcpJobPhasesDesc
50
- memo: List[AcpRequestMemo]
51
- tweet_history : ITweet
52
- last_updated: int
53
-
54
- @dataclass
55
- class IDeliverable:
56
- type: Literal["url", "text"]
57
- value: str
58
-
59
- @dataclass
60
- class IInventory(IDeliverable):
61
- job_id: int
62
-
63
- @dataclass
64
- class AcpJobsSection:
65
- as_a_buyer: List[AcpJob]
66
- as_a_seller: List[AcpJob]
67
-
68
- @dataclass
69
- class AcpJobs:
70
- active: AcpJobsSection
71
- completed: List[AcpJob]
72
- cancelled: List[AcpJob]
73
-
74
- @dataclass
75
- class AcpInventory:
76
- aquired: List[IInventory]
77
- produced: List[IInventory]
78
-
79
- @dataclass
80
- class AcpState:
81
- inventory: AcpInventory
82
- jobs: AcpJobs