Your First Service

Get started with building a DAIN Service by understanding and running the auto-generated code sample.

Now that you've got your working environment set up, let's dive into the auto-generated code a little bit starting with src/index.ts.

Imports

import { z } from "zod";
import axios from "axios";

import {
  defineDAINService,
  ToolConfig,
} from "@dainprotocol/service-sdk";

import { CardUIBuilder, TableUIBuilder, MapUIBuilder } from "@dainprotocol/utils";

The DAIN Service SDK uses Zod for handling the data being passed into various config data structures and any library for handling API requests (in this case, axios).

  • The ToolConfig type is used to define an individual tool that the service provides.
  • The defineDAINService type is used to define the service metadata for the DAIN network.

The default generated service is a working weather data service that requests data from the OpenMeteo Weather API.

CardUIBuilder, TableUIBuilder, and MapUIBuilder are used to build the UI for the service.

Tool Config

A Tool is the most primitive major object in the Service SDK.

const getWeatherConfig: ToolConfig = {
  id: "get-weather",
  name: "Get Weather",
  description: "Fetches current weather for a city",
  input: z
    .object({
      locationName: z.string().describe("Location name"),
      latitude: z.number().describe("Latitude coordinate"),
      longitude: z.number().describe("Longitude coordinate"),
    })
    .describe("Input parameters for the weather request"),
  output: z
    .object({
      temperature: z.number().describe("Current temperature in Celsius"),
      windSpeed: z.number().describe("Current wind speed in km/h"),
    })
    .describe("Current weather information"),
  pricing: { pricePerUse: 0, currency: "USD" },
  handler: async ({ locationName, latitude, longitude }, agentInfo, context) => {
    console.log(
      `User / Agent ${agentInfo.id} requested weather at ${locationName} (${latitude},${longitude})`
    );


    const response = await axios.get(
      `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,wind_speed_10m`
    );

    const { temperature_2m, wind_speed_10m } = response.data.current;
    const weatherEmoji = getWeatherEmoji(temperature_2m);

    return {
      text: `The current temperature in ${locationName} is ${temperature_2m}°C with wind speed of ${wind_speed_10m} km/h`,
      data: {
        temperature: temperature_2m,
        windSpeed: wind_speed_10m,
      },
      ui: new CardUIBuilder()
        .setRenderMode("page")
        .title(`Current Weather in ${locationName} ${weatherEmoji}`)
        .addChild(new MapUIBuilder()
          .setInitialView(latitude, longitude, 10)
          .setMapStyle('mapbox://styles/mapbox/streets-v12')
          .addMarkers([
            {
              latitude,
              longitude,
              title: locationName,
              description: `Temperature: ${temperature_2m}°C\nWind: ${wind_speed_10m} km/h`,
              text: `${locationName} ${weatherEmoji}`,
            }
          ])
          .build())
        .content(`Temperature: ${temperature_2m}°C\nWind Speed: ${wind_speed_10m} km/h`)
        .build(),
    };
  },
};

The following parameters are involved:

  • The id parameter is used for identifying the tool.
  • The name parameter is the name of the tool.
  • The description parameter is a short description of what the tool does.
  • The input parameter is used to define what kind of specific input would be extracted from the natural language input.
  • The output parameter is used to define what kind of output would be produced by the tool.
  • The pricing parameter is irrelevant for now, but it defines how much your service would earn per usage of this tool.
  • The handler part includes the logic for the tool. See Tool Handler Returns for details on what the handler must return.

DAIN Service

Every Service will have one dainService object that looks like this:

const dainService = defineDAINService({
  metadata: {
    title: "Weather DAIN Service",
    description:
      "A DAIN service for current weather and forecasts using Open-Meteo API",
    version: "1.0.0",
    author: "Your Name",
    tags: ["weather", "forecast", "dain"],
    logo: "https://cdn-icons-png.flaticon.com/512/252/252035.png"
  },
  identity: {
    apiKey: process.env.DAIN_API_KEY,
  },
  tools: [getWeatherConfig, getWeatherForecastConfig],
});

The following parameters are involved:

  • The metadata parameter contains the metadata involved in deploying the service to the network. This includes the title of the service, a description, the version that is deployed, the author of the service and tags used in routing to the service.
  • The identity parameter contains the API key for the service that you got in the previous section.
  • The tools parameter contains the Tool config objects of the tools to be deployed with the service.

Starting the Service

Now all that is left is to start the service.

dainService.startNode().then(({ address }) => {
  console.log("DAIN Service is running at :" + address().port);
});

This runs the Service on a port, which is 2022 here.

Then, run this command in the terminal:

npm run dev

or with pnpm:

pnpm run dev

Running this command should generate a tunnel URL for the service that you would need for testing.