Skip to main content
Version: Latest

Own your first AI Human

In this chapter, we will quickly go over AIHuman(AIPlayer) setup process and make the default AI say a sentence. When running AIPlayer for the first time, it may take several minutes to load resources depending on the network condition. The progress of this loading process can be monitored.



1. Include the SDK and Ready the AIPlayer in the web page

<script src="https://cdn-aihuman.deepbrainai.io/sdk/web/aiPlayer-1.5.2.min.js"></script>

<div id="AIPlayerWrapper"></div>
const wrapper = document.getElementById("AIPlayerWrapper");
const AI_PLAYER = new AIPlayer(wrapper);

2. Authenticate the SDK

2.1. Enter appId and get userKey from SDK website

  • Create a project in SDK website
  • Enter the appId for the web appId(recommend unique appId such as your web service’s domain). Then userkey will be issued.

2.2. Create ClientToken API on your Server

  • Next, for SDK authentication, clientToken is needed.
  • The clientToken generation api should be implemented on your server, so that the client can request when it needs.
  • Below is how to install jsonwebtoken lib for JWT on your server.(Note: JWT)
npm install jsonwebtoken
  • Below is an example of creating clientToken from nodejs.
  // generateJWT.js(Server)

import jwt from "jsonwebtoken"

const userKey = "..."; // TODO: userKey input
const payload = {
appId: "...", // TODO: appId input
platform: "web"
};
const options = {
header: { typ: "JWT", alg: "HS256" },
expiresIn: 60 * 5 // expire time: 5 mins
};

function generateJWT(req, res) {
try {
const clientToken = jwt.sign(payload, userKey, options);

res.json({ appId: payload.appId, token: clientToken });
} catch (e) {
console.log("jwt generate err ", e.name, e.message);
}
}
  • Below is an example of routing the generateJWT function for next.js.
  // generateJWT.js(Server) append
export default (req, res) => {
if (req.method === "GET") return generateJWT(req, res);

// if (req.method === "POST") return generateJWT(req, res);
};
  • And Below is an example of routing the generateJWT function for express.
  // generateJWT.js(Server)
module.export = generateJWT;
  const express = require('express');
const app = express();
const generateJWT = require("..."); // TODO: generateJWT.js access path

app.get('/generateJWT', generateJWT);
// app.post('/generateJWT', generateJWT);

2.3. Request generateJWT (requestClientToken) from Client to Server

// quickStart.js(Client)

async function requestClientToken() {
// TODO: Server generateJWT request address input
// for example : const result = await makeRequest("GET", "/api/generateJWT");
const result = await makeRequest("GET", "...");

// Success
DATA.appId = result.appId;
DATA.clientToken = result.token;
// ...
}

2.4. generateToken request

  • After the 'requestClientToken' call is succeeded, then call AIPlayer's "generateToken" function with appId and clientToken. It responds with JSON containing information such as verifiedToken, tokenExpire, and defaultAI. The 'generateToken' call succeeds means the authication is completed. For more info, refer to API section.
  // quickStart.js(Client)

async function generateVerifiedToken() {
const result = await AI_PLAYER.generateToken({ appId: DATA.appId, token: DATA.clientToken });

if (result?.succeed) {
DATA.verifiedToken = result.token;
DATA.tokenExpire = result.tokenExpire;
DATA.defaultAI = result.defaultAI;
}
}

3. Initialize AIPlayer

After successful authentication, the AIPlayer needs to be initialized using the init function. At this time, it can be set with defaultAI. If you want to use other AIs, you can get a list of available AIs through the getAIList function. For details, please refer to AIPlayer section.

// quickStart.js(Client)

await AI_PLAYER.init({
aiName: DATA.defaultAI.ai_name, size: 1.0, left: 0, top: 0, speed: 1.0
});

4. Let's make AIPlayer's callback and monitor the event of AIPlayer

You can set AIPlayer's listener for AI events like below. And notice that you can call send function after the AI initialization has completed.

const AIPlayerState = Object.freeze({
NONE: 0,
INITIALIZE: 1,
IDLE: 2,
PLAY: 3,
PAUSE: 4,
RELEASE: 5
})

let curAIState = AIPlayerState.NONE
function initAIPlayerEvent() {
AI_PLAYER.onAIPlayerEvent = function (aiEvent) {
switch (aiEvent.type) {
//...
case AIEventType.AIPLAYER_STATE_CHANGED:
let newAIState = AI_PLAYER.getState()
if (curAIState == AIPlayerState.INITIALIZE && newAIState == AIPlayerState.IDLE) {
console.log('AI initialization completed.')
// you can send now!
// AI_PLAYER.send("Hello!")
}
curAIState = newAIState
break
//...
}
}
}

5. Full Client Sample Source Code

<!-- quickStart.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>AIPlayer JavaScript SDK Quick Start</title>
</head>
<style>
html,
body,
#AIPlayerWrapper {
height: 100%;
}

.RightLayout {
width: 20vw;
height: 100%;
min-width: 300px;
position: fixed;
bottom: 0;
right: 5%;
}

.InfoContainer {
height: 27vh;
min-height: 150px;
padding-bottom: 3vh;
}

.demoList {
width: 100%;
margin-top: 5vh;
height: 3vh;
min-height: 20px;
display: flex;
justify-content: space-between;
}

.demoList > button {
width: 6vw;
min-width: 90px;
}
</style>

<body style="height: 100%">

<div id="AIPlayerWrapper"></div>

<div style="display: grid; position: fixed; top:2%; left:2%; width: fit-content" id="AIPlayerTexts">
<button onclick="speak(this.innerHTML)">How are you?</button>
<button onclick="speak(this.innerHTML)">Nice to see you!</button>
</div>

<div class="RightLayout">
<div class="InfoContainer">
<div id="demoListInQuickstart" class="demoList">
<button style="background-color: gold">QuickStart</button>
<button onclick="javascript:location.href='./demo1.html'">SDK Demo</button>
<button onclick="javascript:location.href='./demo2.html'">STT Demo</button>
</div>
</div>
</div>

</body>

<!-- add JavaScript SDK -->
<script src="https://cdn-aihuman.deepbrainai.io/sdk/web/aiPlayer-1.5.2.min.js"></script>
<script src="./quickStart.js"></script>
</html>


const wrapper = document.getElementById("AIPlayerWrapper");
const AI_PLAYER = new AIPlayer(wrapper);

const DATA = {};

initSample();

async function initSample() {
initAIPlayerEvent();

await generateClientToken();
await generateVerifiedToken();

await AI_PLAYER.init({
aiName: DATA.defaultAI.ai_name,
size: 1.0,
left: 0,
top: 0,
speed: 1.0,
});
}

// =========================== AIPlayer Setup ================================ //

async function generateClientToken() {
// const result = await makeRequest("GET", "..."); // TODO: Server generateJWT request address input
// TODO: response handling
const result = await makeRequest("GET", "/api/generateJWT"); // TODO: e.g.
console.log('generateClientToken', result)

// Success
DATA.appId = result.appId;
DATA.clientToken = result.token;
// ...
}

async function generateVerifiedToken() {
const result = await AI_PLAYER.generateToken({
appId: DATA.appId,
token: DATA.clientToken,
});
console.log('generateVerifiedToken', result)

if (result?.succeed) {
// TODO: response data handling
DATA.verifiedToken = result.token;
DATA.tokenExpire = result.tokenExpire;
DATA.defaultAI = result.defaultAI;
} else {
// TODO: error handling
console.log('generateVerifiedToken error', result)
}
}

// =========================== AIPlayer Callback ================================ //

function initAIPlayerEvent() {
//AIError & callback
const AIErrorCode = Object.freeze({
AI_API_ERR: 10000,
AI_SERVER_ERR: 11000,
AI_RES_ERR: 12000,
AI_INIT_ERR: 13000,
INVALID_AICLIPSET_ERR: 14000,
AICLIPSET_PRELOAD_ERR: 15000,
AICLIPSET_PLAY_ERR: 16000,
RESERVED_ERR: 17000,
UNKNOWN_ERR: -1,
})

// TODO: AIPlayer error handling
AI_PLAYER.onAIPlayerErrorV2 = function(aiError) {
let codeName = 'UNKNOWN_ERR'
if (aiError.code >= AIErrorCode.RESERVED_ERR) {
codeName = 'RESERVED_ERR'
} else if (aiError.code >= AIErrorCode.AICLIPSET_PLAY_ERR) {
codeName = 'AICLIPSET_PLAY_ERR'
} else if (aiError.code >= AIErrorCode.AICLIPSET_PRELOAD_ERR) {
codeName = 'AICLIPSET_PRELOAD_ERR'
} else if (aiError.code >= AIErrorCode.INVALID_AICLIPSET_ERR) {
codeName = 'INVALID_AICLIPSET_ERR'
} else if (aiError.code >= AIErrorCode.AI_INIT_ERR) {
codeName = 'AI_INIT_ERR'
} else if (aiError.code >= AIErrorCode.AI_RES_ERR) {
codeName = 'AI_RES_ERR'
} else if (aiError.code >= AIErrorCode.AI_SERVER_ERR) {
codeName = 'AI_SERVER_ERR'
} else if (aiError.code >= AIErrorCode.AI_API_ERR) {
codeName = 'AI_API_ERR'
} else if (aiError.code > AIErrorCode.UNKNOWN_ERR) { //0 ~ 9999
codeName = 'BACKEND_ERR'

if (aiError.code == 1402) { //invalid or token expired
refreshTokenIFExpired()
}
}

console.log('onAIPlayerErrorV2', aiError.code, codeName, aiError.message)
}

//AIEvent & callback
const AIEventType = Object.freeze({
RES_LOAD_STARTED: 0,
RES_LOAD_COMPLETED: 1,
AICLIPSET_PLAY_PREPARE_STARTED: 2,
AICLIPSET_PLAY_PREPARE_COMPLETED: 3,
AICLIPSET_PRELOAD_STARTED: 4,
AICLIPSET_PRELOAD_COMPLETED: 5,
AICLIPSET_PRELOAD_FAILED: 6,
AICLIPSET_PLAY_STARTED: 7,
AICLIPSET_PLAY_COMPLETED: 8,
AICLIPSET_PLAY_FAILED: 9,
AI_CONNECTED: 10,
AI_DISCONNECTED: 11,
AICLIPSET_PLAY_BUFFERING: 12,
AICLIPSET_RESTART_FROM_BUFFERING: 13,
AIPLAYER_STATE_CHANGED: 14,
AI_RECONNECT_ATTEMPT: 15,
AI_RECONNECT_FAILED: 16,
UNKNOWN: -1,
})

const AIPlayerState = Object.freeze({
NONE: 0,
INITIALIZE: 1,
IDLE: 2,
PLAY: 3,
PAUSE: 4,
RELEASE: 5
})

let curAIState = AIPlayerState.NONE
AI_PLAYER.onAIPlayerEvent = function (aiEvent) {
let typeName = ""
switch (aiEvent.type) {
case AIEventType.AIPLAYER_STATE_CHANGED:
typeName = 'AIPLAYER_STATE_CHANGED';

let newAIState = AI_PLAYER.getState()
if (curAIState == AIPlayerState.INITIALIZE && newAIState == AIPlayerState.IDLE) {
console.log('AI initialization completed.')
}
curAIState = newAIState
break
case AIEventType.AI_CONNECTED:
typeName = 'AI_CONNECTED';
break
case AIEventType.RES_LOAD_STARTED:
typeName = 'RES_LOAD_STARTED';
break
case AIEventType.RES_LOAD_COMPLETED:
typeName = 'RES_LOAD_COMPLETED';
break
case AIEventType.AICLIPSET_PLAY_PREPARE_STARTED:
typeName = 'AICLIPSET_PLAY_PREPARE_STARTED';
// $('#AIPlayerStateText').text('AI started preparation to speak.');
break
case AIEventType.AICLIPSET_PLAY_PREPARE_COMPLETED:
typeName = 'AICLIPSET_PLAY_PREPARE_COMPLETED';
// $("#AIPlayerStateText").text("AI finished preparation to speak.");
break
case AIEventType.AICLIPSET_PRELOAD_STARTED:
typeName = 'AICLIPSET_PRELOAD_STARTED';
// $("#AIPlayerStateText").text("AI started preparation to preload.");
break
case AIEventType.AICLIPSET_PRELOAD_COMPLETED:
typeName = 'AICLIPSET_PRELOAD_COMPLETED';
// $("#AIPlayerStateText").text("AI finished preparation to preload.");
break
case AIEventType.AICLIPSET_PLAY_STARTED:
typeName = 'AICLIPSET_PLAY_STARTED';
// $("#AIPlayerStateText").text("AI started speaking.");
break
case AIEventType.AICLIPSET_PLAY_COMPLETED:
typeName = 'AICLIPSET_PLAY_COMPLETED';
//$("#AIPlayerStateText").text("AI finished speaking.");
break
case AIEventType.AI_DISCONNECTED:
typeName = 'AI_DISCONNECTED';
// $("#AIPlayerStateText").text("AI Disconnected. Please wait or reconnect");
break
case AIEventType.AICLIPSET_PRELOAD_FAILED:
typeName = 'AICLIPSET_PRELOAD_FAILED';
// $("#AIPlayerStateText").text("AI preload failed.");
break
case AIEventType.AICLIPSET_PLAY_FAILED:
typeName = 'AICLIPSET_PLAY_FAILED';
// $("#AIPlayerStateText").text("AI play failed.");
break
case AIEventType.AICLIPSET_PLAY_BUFFERING:
typeName = 'AICLIPSET_PLAY_BUFFERING';
// $("#AIPlayerStateText").text("AI is buffering.");
break
case AIEventType.AICLIPSET_RESTART_FROM_BUFFERING:
typeName = 'AICLIPSET_RESTART_FROM_BUFFERING';
// $("#AIPlayerStateText").text("AI is restarted from buffering.");
break
case AIEventType.UNKNOWN:
typeName = 'UNKNOWN';
break
}

console.log('onAIPlayerEvent:', aiEvent.type, typeName, 'clipSet:', aiEvent.clipSet)
}

AI_PLAYER.onAIPlayerLoadingProgressed = function (result) {
console.log(`AI Resource Loading... ${result.loading || 0}%`)
};
}

// =========================== AIPlayer Function ================================ //

function speak(text) {
AI_PLAYER.send(text);
}

// =========================== ETC ================================ //

// sample Server request function
async function makeRequest(method, url, params) {
const options = {
method,
headers: { "Content-Type": "application/json; charSet=utf-8" },
};

if (method === "POST") options.body = JSON.stringify(params || {});

return fetch(url, options)
.then((response) => response.json())
.then((data) => data)
.catch((error) => {
console.error("** An error occurred during the fetch", error);
// showPop(
// "Generate Client Token Error",
// `no client token can be generated.`
// );
return undefined;
});
}


6. Full Server Sample Source Code

// generateJWT.js(Server)

import jwt from "jsonwebtoken"

const userKey = "..."; // TODO: userKey input
const payload = {
appId: "...", // TODO: appId input
platform: "web"
};
const options = {
header: { typ: "JWT", alg: "HS256" },
expiresIn: 60 * 5 // expire time: 5 mins
};

function generateJWT(req, res) {
try {
const clientToken = jwt.sign(payload, userKey, options);

res.json({ token: clientToken, appId: payload.appId });
} catch (e) {
console.log("jwt generate err ", e.name, e.message);
}
}

// next.js
export default (req, res) => {
if (req.method === "GET") return generateJWT(req, res);
};