|
1 | | -import importlib.util |
2 | 1 | from typing import Any, Optional, cast |
3 | 2 |
|
4 | 3 | import typer |
5 | | -import uvicorn |
6 | 4 | from typer import Context |
7 | 5 |
|
8 | 6 | from torus._common import intersection_update |
| 7 | +from torus.balance import from_nano |
9 | 8 | from torus.cli._common import ( |
10 | 9 | make_custom_context, |
11 | 10 | print_module_info, |
12 | 11 | print_table_from_plain_dict, |
| 12 | + render_pydantic_table, |
13 | 13 | ) |
14 | 14 | from torus.errors import ChainTransactionError |
15 | 15 | from torus.key import check_ss58_address |
16 | | -from torus.misc import get_map_modules |
17 | | -from torus.module._rate_limiters.limiters import ( |
18 | | - IpLimiterParams, |
19 | | - StakeLimiterParams, |
20 | | -) |
21 | | -from torus.module.server import ModuleServer |
| 16 | +from torus.misc import get_governance_config, get_map_modules |
22 | 17 | from torus.types import Ss58Address |
23 | 18 |
|
24 | 19 | module_app = typer.Typer(no_args_is_help=True) |
@@ -89,6 +84,73 @@ def add_to_whitelist(ctx: Context, curator_key: str, agent_key: str): |
89 | 84 | context.info(f"Module {agent_key} added to whitelist") |
90 | 85 |
|
91 | 86 |
|
| 87 | +@module_app.command() |
| 88 | +def list_applications(ctx: Context): |
| 89 | + """ |
| 90 | + Lists all registered applications. |
| 91 | + """ |
| 92 | + context = make_custom_context(ctx) |
| 93 | + client = context.com_client() |
| 94 | + |
| 95 | + with context.progress_status("Getting applications..."): |
| 96 | + applications = client.query_map_applications() |
| 97 | + render_pydantic_table( |
| 98 | + [*applications.values()], context.console, title="Applications" |
| 99 | + ) |
| 100 | + |
| 101 | + |
| 102 | +@module_app.command() |
| 103 | +def add_application( |
| 104 | + ctx: Context, |
| 105 | + payer_key: str, |
| 106 | + application_key: str, |
| 107 | + data: str, |
| 108 | + removing: bool = False, |
| 109 | +): |
| 110 | + """ |
| 111 | + Adds an application to a module. |
| 112 | + """ |
| 113 | + context = make_custom_context(ctx) |
| 114 | + client = context.com_client() |
| 115 | + |
| 116 | + resolved_key = context.load_key(payer_key, None) |
| 117 | + application_addr = context.resolve_key_ss58(application_key, None) |
| 118 | + application_burn = get_governance_config(client).agent_application_cost |
| 119 | + confirm = context.confirm( |
| 120 | + f"{from_nano(application_burn)} tokens will be burned. Do you want to continue?" |
| 121 | + ) |
| 122 | + if not confirm: |
| 123 | + context.info("Application addition cancelled") |
| 124 | + return |
| 125 | + with context.progress_status(f"Adding application {application_key}..."): |
| 126 | + client.add_application( |
| 127 | + key=resolved_key, |
| 128 | + application_key=application_addr, |
| 129 | + data=data, |
| 130 | + removing=removing, |
| 131 | + ) |
| 132 | + context.info("Application added.") |
| 133 | + |
| 134 | + |
| 135 | +@module_app.command() |
| 136 | +def accept_application( |
| 137 | + ctx: Context, |
| 138 | + curator_key: str, |
| 139 | + application_id: int, |
| 140 | +): |
| 141 | + """ |
| 142 | + Accepts an application. |
| 143 | + """ |
| 144 | + context = make_custom_context(ctx) |
| 145 | + client = context.com_client() |
| 146 | + |
| 147 | + resolved_curator_key = context.load_key(curator_key, None) |
| 148 | + |
| 149 | + with context.progress_status("Accepting application..."): |
| 150 | + client.accept_application(resolved_curator_key, application_id) |
| 151 | + context.info("Application accepted.") |
| 152 | + |
| 153 | + |
92 | 154 | @module_app.command() |
93 | 155 | def deregister(ctx: Context, key: str): |
94 | 156 | """ |
@@ -172,104 +234,6 @@ def update( |
172 | 234 | raise ChainTransactionError(response.error_message) # type: ignore |
173 | 235 |
|
174 | 236 |
|
175 | | -@module_app.command() |
176 | | -def serve( |
177 | | - ctx: typer.Context, |
178 | | - class_path: str, |
179 | | - key: str, |
180 | | - port: int = 8000, |
181 | | - ip: Optional[str] = None, |
182 | | - whitelist: Optional[list[str]] = None, |
183 | | - blacklist: Optional[list[str]] = None, |
184 | | - ip_blacklist: Optional[list[str]] = None, |
185 | | - test_mode: Optional[bool] = False, |
186 | | - request_staleness: int = typer.Option(120), |
187 | | - use_ip_limiter: Optional[bool] = typer.Option( |
188 | | - False, help=("If this value is passed, the ip limiter will be used") |
189 | | - ), |
190 | | - token_refill_rate_base_multiplier: Optional[int] = typer.Option( |
191 | | - None, |
192 | | - help=( |
193 | | - "Multiply the base limit per stake. Only used in stake limiter mode." |
194 | | - ), |
195 | | - ), |
196 | | -): |
197 | | - """ |
198 | | - Serves a module on `127.0.0.1` on port `port`. `class_path` should specify |
199 | | - the dotted path to the module class e.g. `module.submodule.ClassName`. |
200 | | - """ |
201 | | - context = make_custom_context(ctx) |
202 | | - use_testnet = context.get_use_testnet() |
203 | | - path_parts = class_path.split(".") |
204 | | - match path_parts: |
205 | | - case [*module_parts, class_name]: |
206 | | - module_path = ".".join(module_parts) |
207 | | - if not module_path: |
208 | | - # This could do some kind of relative import somehow? |
209 | | - raise ValueError( |
210 | | - f"Invalid class path: `{class_path}`, module name is missing" |
211 | | - ) |
212 | | - if not class_name: |
213 | | - raise ValueError( |
214 | | - f"Invalid class path: `{class_path}`, class name is missing" |
215 | | - ) |
216 | | - case _: |
217 | | - # This is impossible |
218 | | - raise Exception(f"Invalid class path: `{class_path}`") |
219 | | - |
220 | | - try: |
221 | | - module = importlib.import_module(module_path) |
222 | | - except ModuleNotFoundError: |
223 | | - context.error(f"Module `{module_path}` not found") |
224 | | - raise typer.Exit(code=1) |
225 | | - |
226 | | - try: |
227 | | - class_obj = getattr(module, class_name) |
228 | | - except AttributeError: |
229 | | - context.error(f"Class `{class_name}` not found in module `{module}`") |
230 | | - raise typer.Exit(code=1) |
231 | | - |
232 | | - keypair = context.load_key(key, None) |
233 | | - |
234 | | - token_refill_rate = token_refill_rate_base_multiplier or 1 |
235 | | - limiter_params = ( |
236 | | - IpLimiterParams() |
237 | | - if use_ip_limiter |
238 | | - else StakeLimiterParams(token_ratio=token_refill_rate) |
239 | | - ) |
240 | | - |
241 | | - if whitelist is None: |
242 | | - context.info( |
243 | | - "WARNING: No whitelist provided, will accept calls from any key" |
244 | | - ) |
245 | | - |
246 | | - try: |
247 | | - whitelist_ss58 = list_to_ss58(whitelist) |
248 | | - except AssertionError: |
249 | | - context.error("Invalid SS58 address passed to whitelist") |
250 | | - exit(1) |
251 | | - try: |
252 | | - blacklist_ss58 = list_to_ss58(blacklist) |
253 | | - except AssertionError: |
254 | | - context.error("Invalid SS58 address passed on blacklist") |
255 | | - exit(1) |
256 | | - cast(list[Ss58Address] | None, whitelist) |
257 | | - |
258 | | - server = ModuleServer( |
259 | | - class_obj(), |
260 | | - keypair, |
261 | | - whitelist=whitelist_ss58, |
262 | | - blacklist=blacklist_ss58, |
263 | | - max_request_staleness=request_staleness, |
264 | | - limiter=limiter_params, |
265 | | - ip_blacklist=ip_blacklist, |
266 | | - use_testnet=use_testnet, |
267 | | - ) |
268 | | - app = server.get_fastapi_app() |
269 | | - host = ip or "127.0.0.1" |
270 | | - uvicorn.run(app, host=host, port=port) # type: ignore |
271 | | - |
272 | | - |
273 | 237 | @module_app.command() |
274 | 238 | def info(ctx: Context, name: str, balance: bool = False): |
275 | 239 | """ |
|
0 commit comments