The SingularityNET network allows developers to register their AI services on an open marketplace and charge for access. Though the expectation is that service consumers will primarily call services from code, the SingularityNET Dapp offers a rich UI/UX for people to explore the services offered on the network. Providing an interface for consumers to interact with the service on the marketplace is a big driver for demand.
SingularityNet storybook is a UI component explorer for service developers. This lists all common components used in the marketplace and can be reused for your service.
Install Node.js and npm and yarn
Download the snet-dapp code
git clone git@github.com:singnet/snet-dapp.git
cd snet-dapp
yarn install
cp .env.sandbox .env
Update .env
file
REACT_APP_SANDBOX_SERVICE_ENDPOINT
The daemon endpoint for call AI methods of your service.
REACT_APP_SANDBOX_ORG_ID
& REACT_APP_SANDBOX_SERVICE_ID
The org_id
to which the service belongs and the service_id
of the service. The values set for these variables will be used for registering the custom ui.
Start the AI service
Start the AI service locally along with the snet daemon. Make sure the blockchain is disabled in the daemon configuration.
Building the custom UI
protoc-gen-ts
in the path ./node_modules/.bin/
. Else run npm i -D ts-protoc-gen
Generate js
stubs from .proto
files.
For the custom UI to talk to the services on SingularityNET platform via the DApp, we are using gRPC-web by improbable-eng. Apart from the steps mentioned at the official documentation to generate js stubs
from .proto
definitions, you also need to provide the namespace_prefix
flag to the generator.
Here is an example which illustrates the usage
For Linux
protoc \
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--js_out=import_style=commonjs,binary,namespace_prefix=\
[package name]_[org id]_[service]:. --ts_out=service=grpc-web:. \
[proto file name].proto
For Windows CMD
protoc ^
--plugin=protoc-gen-ts=%cd%/node_modules/.bin/protoc-gen-ts.cmd ^ --js_out=import_style=commonjs,binary,namespace_prefix=^
[package name]_[org id]_[service]:. --ts_out=service=grpc-web:. ^
[proto file name].proto
*pb.js
and *pb_service.js
into your component’s folder and ignore the rest./**eslint-disable */
org-id
to which this service belongs inside src/assets/thirdPartyServices
. It could be possible that the directory already exists, in which case you can use it instead of creating a new one.service-id
under the newly created directory in the above step e.g. for a service with org-id: snet and service-id: example-service you will have to do the following assuming you are at the root of the snet-dapp
cd src/assets/thirdPartyServices
mkdir snet
cd snet
mkdir example_service
cd example_service
js stubs
Register the custom UI
Add an entry for the new service in src/assets/thirdPartyServices/index.js
if it does not already exist. Add the following line towards the end of the file. Make sure to replace orgId
, serviceId
and CustomUIComponent
accordingly.
thirdPartyCustomUIComponents.addCustomUIComponen(orgId, serviceId, CustomUIComponent);
For example with org_id=”snet” and service_id=”example-service”:
thirdPartyCustomUIComponents.addCustomUIComponent("snet", "example_service", ExampleServiceUI);
Run the DApp
Assuming that the snet daemon is running on the port that you specified in the REACT_APP_SANDBOX_SERVICE_ENDPOINT, running the bellow commands should bring up the DApp in sandbox mode for local development.
yarn start
or
npm run start
Component development
To request a call, your component should use the props service Client
and isComplete
.
serviceClient
provides methods for calling the service.isComplete
provides your service with information about the service’s response.
Example of a call service:import { ExampleService } from "./example_pb_service";
const ExampleServiceUI = ({ serviceClient, isComplete }) => {
<...>
const parseResponse = (response) => {
const { message, status, statusMessage } = response;
if (status !== 0) {
throw new Error(statusMessage);
}
setResponse(message.getResponse());
};
const runService = () => {
const methodDescriptor = ExampleService.exampleServiceMethod;
const request = new methodDescriptor.requestType();
request.setUserInput(userInput);
const props = {
request,
onEnd: (response) => parseResponse(response),
};
//serviceClient is prop
serviceClient.unary(methodDescriptor, props);
};
<...>
//isComplete is prop
if (!isComplete) {
return <ServiceInput />;
} else {
return <ServiceOutput />;
}
};
The load indication is enabled using the Marketplace, you do not need to develop this part of the application.
Last modified on : 15-Oct-24