diff --git a/snet/sdk/__init__.py b/snet/sdk/__init__.py index 850e1bc..f4406e0 100644 --- a/snet/sdk/__init__.py +++ b/snet/sdk/__init__.py @@ -95,8 +95,8 @@ def create_service_client(self, org_id: str, service_id: str, group_name=None, lib_generator.generate_client_library() else: path_to_pb_files = self.get_path_to_pb_files(org_id, service_id) - pb_2_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2.py") - pb_2_grpc_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2_grpc.py") + pb_2_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2.py", exclude=['training', 'pricing']) + pb_2_grpc_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2_grpc.py", exclude=['training', 'pricing']) if not pb_2_file_name or not pb_2_grpc_file_name: lib_generator.generate_client_library() @@ -145,7 +145,7 @@ def get_path_to_pb_files(self, org_id: str, service_id: str) -> str: def get_module_by_keyword(self, org_id: str, service_id: str, keyword: str) -> ModuleName: path_to_pb_files = self.get_path_to_pb_files(org_id, service_id) - file_name = find_file_by_keyword(path_to_pb_files, keyword) + file_name = find_file_by_keyword(path_to_pb_files, keyword, exclude=['training', 'pricing']) module_name = os.path.splitext(file_name)[0] return ModuleName(module_name) diff --git a/snet/sdk/account.py b/snet/sdk/account.py index df71493..f413a35 100644 --- a/snet/sdk/account.py +++ b/snet/sdk/account.py @@ -56,9 +56,9 @@ def _get_gas_price(self): gas_price = self.web3.eth.gas_price if gas_price <= 15000000000: gas_price += gas_price * 1 / 3 - elif gas_price > 15000000000 and gas_price <= 50000000000: + elif 15000000000 < gas_price <= 50000000000: gas_price += gas_price * 1 / 5 - elif gas_price > 50000000000 and gas_price <= 150000000000: + elif 50000000000 < gas_price <= 150000000000: gas_price += 7000000000 elif gas_price > 150000000000: gas_price += gas_price * 1 / 10 diff --git a/snet/sdk/client_lib_generator.py b/snet/sdk/client_lib_generator.py index 3933ba1..21c60a9 100644 --- a/snet/sdk/client_lib_generator.py +++ b/snet/sdk/client_lib_generator.py @@ -1,9 +1,10 @@ import os from pathlib import Path, PurePath +import shutil from snet.sdk import StorageProvider from snet.sdk.utils import ipfs_utils -from snet.sdk.utils.utils import compile_proto, type_converter +from snet.sdk.utils.utils import compile_proto, type_converter, RESOURCES_PATH class ClientLibGenerator: @@ -41,6 +42,8 @@ def generate_client_library(self): # Receive proto files self._metadata_provider.fetch_and_extract_proto(service_api_source, library_dir_path) + self.check_protos_and_copy(library_dir_path) + # Compile proto files compile_proto(Path(library_dir_path), library_dir_path, target_language=self.language) @@ -48,3 +51,19 @@ def generate_client_library(self): f'generated at {library_dir_path}') except Exception as e: print(e) + + def check_protos_and_copy(self, lib_dir_path): + source_dir = RESOURCES_PATH.joinpath("proto") + target_dir = lib_dir_path + + files_to_check = ["training.proto", "pricing.proto"] + + missing_files = [] + for file in files_to_check: + if not os.path.exists(os.path.join(target_dir, file)): + missing_files.append(file) + + for file in missing_files: + source_path = os.path.join(source_dir, file) + target_path = os.path.join(target_dir, file) + shutil.copy2(source_path, target_path) diff --git a/snet/sdk/resources/proto/pricing.proto b/snet/sdk/resources/proto/pricing.proto new file mode 100644 index 0000000..d39177b --- /dev/null +++ b/snet/sdk/resources/proto/pricing.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +package pricing; + +option go_package = "../pricing"; + +extend google.protobuf.MethodOptions { + EstimatePrice my_method_option = 9999127; +} +//In Order to Support Dynamic Pricing , we need a way to indicate which of the methods will need dynamic pricing Support +//By Dynamic Pricing , the pricing of the service call will be a variable of the inputs passed. +//The Service Provider is best positioned to determine this +//The below standards are to be followed +// 1) The Input Message for the Pricing RPC call will be exactly same as the Input Message of the Service Call +// 2) The Output Message for the Pricing RPC MUST return PriceInCogs. +// 3) Define the name of the RPC method to be called using Method Option EstimatePrice + + +//helps determine which method to call for dynamic pricing, +//format is of type "packageName/serviceName/MethodName", Example :"/example_service.Calculator/estimate_add" +//Daemon will invoke the actual RPC method , but will invoke the method defined in the method options before +//to determine the price that needs to be charged for this call +message EstimatePrice { + string estimatePriceMethod = 1; +} +//The Value returned by the Pricing call +message PriceInCogs { + uint64 price = 1; +} \ No newline at end of file diff --git a/snet/sdk/resources/proto/pricing_pb2.py b/snet/sdk/resources/proto/pricing_pb2.py new file mode 100644 index 0000000..bef5eca --- /dev/null +++ b/snet/sdk/resources/proto/pricing_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: pricing.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rpricing.proto\x12\x07pricing\x1a google/protobuf/descriptor.proto\",\n\rEstimatePrice\x12\x1b\n\x13\x65stimatePriceMethod\x18\x01 \x01(\t\"\x1c\n\x0bPriceInCogs\x12\r\n\x05price\x18\x01 \x01(\x04:S\n\x10my_method_option\x12\x1e.google.protobuf.MethodOptions\x18\x97\xa6\xe2\x04 \x01(\x0b\x32\x16.pricing.EstimatePriceB\x0cZ\n../pricingb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pricing_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z\n../pricing' + _globals['_ESTIMATEPRICE']._serialized_start=60 + _globals['_ESTIMATEPRICE']._serialized_end=104 + _globals['_PRICEINCOGS']._serialized_start=106 + _globals['_PRICEINCOGS']._serialized_end=134 +# @@protoc_insertion_point(module_scope) diff --git a/snet/sdk/resources/proto/pricing_pb2_grpc.py b/snet/sdk/resources/proto/pricing_pb2_grpc.py new file mode 100644 index 0000000..2daafff --- /dev/null +++ b/snet/sdk/resources/proto/pricing_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/snet/sdk/resources/proto/training.proto b/snet/sdk/resources/proto/training.proto index 86b3074..f93b3d4 100644 --- a/snet/sdk/resources/proto/training.proto +++ b/snet/sdk/resources/proto/training.proto @@ -28,7 +28,6 @@ message ModelDetails { //set this to true if you want your model to be used by other AI consumers bool is_publicly_accessible = 14; - } message AuthorizationDetails { @@ -91,12 +90,11 @@ message UpdateModelRequest { message ModelDetailsResponse { Status status = 1; ModelDetails model_details = 2; - } service Model { - // The AI developer needs to Implement this service and Daemon will call these + // The AI developer needs to Implement this service (do not copy this in your service proto) and Daemon will call these // There will be no cost borne by the consumer in calling these methods, // Pricing will apply when you actually call the training methods defined. // AI consumer will call all these methods @@ -104,7 +102,8 @@ service Model { rpc delete_model(UpdateModelRequest) returns (ModelDetailsResponse) {} rpc get_model_status(ModelDetailsRequest) returns (ModelDetailsResponse) {} - // Daemon will implement , however the AI developer should skip implementing these and just provide dummy code. + + // Daemon will implement, however the AI developer should skip implementing these and just provide dummy code. rpc update_model_access(UpdateModelRequest) returns (ModelDetailsResponse) {} rpc get_all_models(AccessibleModelsRequest) returns (AccessibleModelsResponse) {} diff --git a/snet/sdk/resources/proto/training_pb2_grpc.py b/snet/sdk/resources/proto/training_pb2_grpc.py index 51ccf27..4976f73 100644 --- a/snet/sdk/resources/proto/training_pb2_grpc.py +++ b/snet/sdk/resources/proto/training_pb2_grpc.py @@ -45,7 +45,7 @@ class ModelServicer(object): """Missing associated documentation comment in .proto file.""" def create_model(self, request, context): - """The AI developer needs to Implement this service and Daemon will call these + """The AI developer needs to Implement this service (do not copy this in your service proto) and Daemon will call these There will be no cost borne by the consumer in calling these methods, Pricing will apply when you actually call the training methods defined. AI consumer will call all these methods @@ -67,7 +67,7 @@ def get_model_status(self, request, context): raise NotImplementedError('Method not implemented!') def update_model_access(self, request, context): - """Daemon will implement , however the AI developer should skip implementing these and just provide dummy code. + """Daemon will implement, however the AI developer should skip implementing these and just provide dummy code. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') diff --git a/snet/sdk/service_client.py b/snet/sdk/service_client.py index 1423266..a0a4895 100644 --- a/snet/sdk/service_client.py +++ b/snet/sdk/service_client.py @@ -201,7 +201,7 @@ def get_path_to_pb_files(self, org_id: str, service_id: str) -> str: def get_services_and_messages_info(self): # Get proto file filepath and open it path_to_pb_files = self.get_path_to_pb_files(self.org_id, self.service_id) - proto_file_name = find_file_by_keyword(path_to_pb_files, keyword=".proto") + proto_file_name = find_file_by_keyword(path_to_pb_files, keyword=".proto", exclude=['training', 'pricing']) proto_filepath = os.path.join(path_to_pb_files, proto_file_name) with open(proto_filepath, 'r') as file: proto_content = file.read() diff --git a/snet/sdk/utils/utils.py b/snet/sdk/utils/utils.py index 21c4369..0793102 100644 --- a/snet/sdk/utils/utils.py +++ b/snet/sdk/utils/utils.py @@ -142,10 +142,12 @@ def __exit__(self, exc_type, exc_value, traceback): pass -def find_file_by_keyword(directory, keyword): +def find_file_by_keyword(directory, keyword, exclude=None): + if exclude is None: + exclude = [] for root, dirs, files in os.walk(directory): for file in files: - if keyword in file: + if keyword in file and all(e not in file for e in exclude): return file