diff --git a/bridges/nodejs/src/main.ts b/bridges/nodejs/src/main.ts index 4a3747aa..67d90328 100644 --- a/bridges/nodejs/src/main.ts +++ b/bridges/nodejs/src/main.ts @@ -1,71 +1,45 @@ -import fs from 'node:fs' import path from 'node:path' -const { - argv: [, , INTENT_OBJ_FILE_PATH] -} = process - +import { getIntentObject } from '@bridge/utils' ;(async (): Promise => { - if (INTENT_OBJ_FILE_PATH) { - const { - domain, - skill, - action, - lang, - utterance, - current_entities: currentEntities, - entities, - current_resolvers: currentResolvers, - resolvers, - slots - } = JSON.parse(await fs.promises.readFile(INTENT_OBJ_FILE_PATH, 'utf8')) + const { + domain, + skill, + action, + lang, + utterance, + current_entities: currentEntities, + entities, + current_resolvers: currentResolvers, + resolvers, + slots + } = await getIntentObject() - const params = { - lang, - utterance, - currentEntities, - entities, - currentResolvers, - resolvers, - slots - } + const params = { + lang, + utterance, + currentEntities, + entities, + currentResolvers, + resolvers, + slots + } - try { - const { [action]: actionFunction } = await import( - path.join( - process.cwd(), - 'skills', - domain, - skill, - 'src', - 'actions', - `${action}.ts` - ) + try { + const { [action]: actionFunction } = await import( + path.join( + process.cwd(), + 'skills', + domain, + skill, + 'src', + 'actions', + `${action}.ts` ) + ) - const speech = actionFunction(params) - - console.log( - JSON.stringify({ - domain, - skill, - action, - lang, - utterance, - entities, - slots, - // TODO - output: { - type: 'end', - codes: '', - speech, - core: {}, - options: {} - } - }) - ) - } catch (e) { - console.error('Error while running action:', e) - } + actionFunction(params) + } catch (e) { + console.error('Error while running action:', e) } })() diff --git a/bridges/nodejs/src/sdk/answer.ts b/bridges/nodejs/src/sdk/answer.ts new file mode 100644 index 00000000..e03f28c5 --- /dev/null +++ b/bridges/nodejs/src/sdk/answer.ts @@ -0,0 +1,84 @@ +import type { ActionResponse } from '@bridge/sdk/types' +import { getIntentObject } from '@bridge/utils' +import { AnswerTypes } from '@bridge/types' + +/** + * Holds methods to communicate data from the skill to the core + */ + +abstract class Answer { + /** + * Send an answer to the core + * @param text + */ + protected abstract text(text: string): Promise + + /** + * Create an answer object to send an answer to the core + * @param type The type of the answer + * @param text The text to send + */ + protected async createAnswerObject( + type: AnswerTypes, + text: string + ): Promise { + try { + const answer = { + ...(await getIntentObject()), + output: { + type, + codes: '', // TODO + speech: text, + core: {}, // TODO + options: {} // TODO + } + } + + process.stdout.write(JSON.stringify(answer)) + + return answer + } catch (e) { + console.error('Error creating answer object:', e) + + return null + } + } +} + +export class IntermediateAnswer extends Answer { + /** + * Create an answer object with the intermediate type + * to send an intermediate answer to the core. + * Used to send an answer before the final answer + * @param text The text to send + * @example await new IntermediateAnswer().text('intermediate answer') + */ + public async text(text: string): Promise { + try { + return await this.createAnswerObject(AnswerTypes.Intermediate, text) + } catch (e) { + console.error('Error creating intermediate answer:', e) + + return null + } + } +} + +export class FinalAnswer extends Answer { + /** + * Create an answer object with the final type + * to send a final answer to the core. + * Used to send an answer before the end of the skill action + * @param text The text to send + * @example await new FinalAnswer().text('final answer') + */ + public async text(text: string): Promise { + try { + return await this.createAnswerObject(AnswerTypes.Final, text) + } catch (e) { + console.error('Error creating final answer:', e) + + return null + } + } +} diff --git a/bridges/nodejs/src/sdk/testo.ts b/bridges/nodejs/src/sdk/testo.ts deleted file mode 100644 index 32f20dcb..00000000 --- a/bridges/nodejs/src/sdk/testo.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function rand(): number { - return Math.random() -} diff --git a/bridges/nodejs/src/sdk/types.ts b/bridges/nodejs/src/sdk/types.ts new file mode 100644 index 00000000..cf3aacc7 --- /dev/null +++ b/bridges/nodejs/src/sdk/types.ts @@ -0,0 +1,3 @@ +import type { AnswerObject } from '@bridge/types' + +export type ActionResponse = AnswerObject | null diff --git a/bridges/nodejs/src/types.ts b/bridges/nodejs/src/types.ts new file mode 100644 index 00000000..31d5281a --- /dev/null +++ b/bridges/nodejs/src/types.ts @@ -0,0 +1,28 @@ +export enum AnswerTypes { + Intermediate = 'inter', + Final = 'end' +} + +export interface IntentObject { + id: string + domain: string + skill: string + action: string + lang: string + utterance: string + current_entities: unknown[] // TODO + entities: unknown[] // TODO + current_resolvers: unknown[] // TODO + resolvers: unknown[] // TODO + slots: Record[] // TODO +} + +export interface AnswerObject extends IntentObject { + output: { + type: AnswerTypes + codes: string + speech: string + core: unknown // TODO + options: unknown // TODO + } +} diff --git a/bridges/nodejs/src/utils.ts b/bridges/nodejs/src/utils.ts new file mode 100644 index 00000000..a0bcd7a2 --- /dev/null +++ b/bridges/nodejs/src/utils.ts @@ -0,0 +1,17 @@ +import fs from 'node:fs' + +import type { IntentObject } from '@bridge/types' + +const { + argv: [, , INTENT_OBJ_FILE_PATH] +} = process + +/** + * Get the intent object from the temporary intent file + * @example await getIntentObject() // { ... } + */ +export async function getIntentObject(): Promise { + return JSON.parse( + await fs.promises.readFile(INTENT_OBJ_FILE_PATH as string, 'utf8') + ) +} diff --git a/bridges/nodejs/tsconfig.json b/bridges/nodejs/tsconfig.json index 16617958..27e1eda0 100644 --- a/bridges/nodejs/tsconfig.json +++ b/bridges/nodejs/tsconfig.json @@ -3,7 +3,10 @@ "compilerOptions": { "rootDir": "./src", "outDir": "./dist/bin", - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@bridge/*": ["./src/*"] + } }, "ts-node": { "swc": true, diff --git a/skills/leon/age/src/actions/run.ts b/skills/leon/age/src/actions/run.ts index 743ac3d5..5b38ee39 100644 --- a/skills/leon/age/src/actions/run.ts +++ b/skills/leon/age/src/actions/run.ts @@ -1,5 +1,8 @@ -import { rand } from '@sdk/testo' +import type { ActionResponse } from '@sdk/types' +import { IntermediateAnswer, FinalAnswer } from '@sdk/answer' -export function run(): string { - return `hello ${rand()}` +export async function run(): Promise { + await new IntermediateAnswer().text('intermediate answer') + + return await new FinalAnswer().text('final answer') } diff --git a/skills/tsconfig.json b/skills/tsconfig.json index 34b397eb..42f752e7 100644 --- a/skills/tsconfig.json +++ b/skills/tsconfig.json @@ -2,6 +2,7 @@ "extends": "@tsconfig/node16-strictest/tsconfig.json", "compilerOptions": { "paths": { + "@bridge/*": ["../bridges/nodejs/src/*"], "@sdk/*": ["../bridges/nodejs/src/sdk/*"] } } diff --git a/tsconfig.json b/tsconfig.json index c047cd49..c7e79259 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "paths": { "@@/*": ["./*"], "@/*": ["./server/src/*"], + "@bridge/*": ["./bridges/nodejs/src/*"], "@sdk/*": ["./bridges/nodejs/src/sdk/*"] }, "ignoreDeprecations": "5.0",