Skip to content

Commit bcd6dde

Browse files
Merge pull request #66 from Capstone-Projects-2025-Fall/AAC-102
added synonym response feedback for developers (AAC-102)
2 parents b8b35e9 + 0c576af commit bcd6dde

3 files changed

Lines changed: 310 additions & 168 deletions

File tree

src/AACVoiceAPI.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { showHistoryPopup } from "./showHistoryPopup";
33
import { SpeechConverterInterface } from "./SpeechConverterInterface";
44
import { SpeechConverterOffline } from "./SpeechConverterOffline";
55
import { SpeechConverterOnline } from "./SpeechConverterOnline";
6-
import { CommandMapping } from "./commandMapping";
6+
import { CommandMapping, CommandAddResult } from "./commandMapping";
77
import { ConfidenceConfig } from './ConfidenceConfig';
88
import { CommandConverter} from './CommandConverter';
99
import { Logger, LogEntry } from './Logging';
@@ -238,7 +238,7 @@ export class AACVoiceAPI{
238238
* @param {string} options.description: A short explanation of what the command does.
239239
* @param {boolean} options.active: Whether the command is currently active. (true or false)
240240
* @param {boolean} options.fetchSynonyms: Whether to automatically fetch synonyms (default: true)
241-
* @returns Promise<boolean> true if successfully added
241+
* @returns Promise<CommandAddResult> Result object with command and synonym information
242242
*/
243243
public async addVoiceCommand(
244244
name: string,
@@ -249,12 +249,18 @@ export class AACVoiceAPI{
249249
fetchSynonyms?: boolean;
250250

251251
}
252-
): Promise<boolean> {
252+
): Promise<CommandAddResult> {
253253
if (!this.mapping) {
254-
return false;
254+
return {
255+
success: false,
256+
commandName: name,
257+
synonymsMapped: [],
258+
synonymCount: 0,
259+
error: 'CommandMapping not initialized'
260+
};
255261
}
256262

257-
return await this.mapping?.addCommand(name,action, options);
263+
return await this.mapping.addCommand(name, action, options);
258264
}
259265

260266
/**

src/commandMapping.ts

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
import { CommandLibrary, GameCommand } from './commandLibrary';
22
import { SynonymResolver } from './SynonymResolver';
33

4+
/**
5+
* Result object returned when adding a command with synonym fetching.
6+
* Provides developers with information about what synonyms were registered.
7+
*/
8+
export interface CommandAddResult {
9+
/** Whether the command was successfully added */
10+
success: boolean;
11+
/** The command name that was added */
12+
commandName: string;
13+
/** Array of synonyms that were successfully registered */
14+
synonymsMapped: string[];
15+
/** Total number of synonyms mapped */
16+
synonymCount: number;
17+
/** Error message if command addition failed */
18+
error?: string;
19+
}
420
/**
521
* CommandMapping provides a simple interface for developers to add and remove
622
* commands from the CommandLibrary.
@@ -51,7 +67,7 @@ export class CommandMapping {
5167
* @param {string} options.description - Description of what the command does
5268
* @param {boolean} options.active - Whether the command is active (default: true)
5369
* @param {boolean} options.fetchSynonyms - Whether to auto-fetch synonyms (default: true)
54-
* @returns {Promise<boolean>} Returns true if command was added successfully, false if duplicate or invalid
70+
* @returns {Promise<CommandAddResult>} Result object with command and synonym information
5571
*/
5672
public async addCommand(
5773
name: string,
@@ -60,19 +76,30 @@ export class CommandMapping {
6076
description?: string;
6177
active?: boolean;
6278
fetchSynonyms?: boolean;
63-
// icon?: unknown; // Uncomment if you reintroduce `icon` in the interface
6479
}
65-
): Promise<boolean> {
80+
): Promise<CommandAddResult> {
6681
const normalized = this.normalize(name);
6782

6883
if (!normalized) {
6984
console.error('Command name cannot be empty');
70-
return false;
85+
return {
86+
success: false,
87+
commandName: name,
88+
synonymsMapped: [],
89+
synonymCount: 0,
90+
error: 'Command name cannot be empty'
91+
};
7192
}
7293

7394
if (this.library.has(normalized)) {
7495
console.warn(`Command "${normalized}" already exists`);
75-
return false;
96+
return {
97+
success: false,
98+
commandName: normalized,
99+
synonymsMapped: [],
100+
synonymCount: 0,
101+
error: `Command "${normalized}" already exists`
102+
};
76103
}
77104

78105
const cmd: GameCommand = {
@@ -86,22 +113,36 @@ export class CommandMapping {
86113

87114
if (!ok) {
88115
console.warn(`Failed to add command "${normalized}"`);
89-
return false;
116+
return {
117+
success: false,
118+
commandName: normalized,
119+
synonymsMapped: [],
120+
synonymCount: 0,
121+
error: `Failed to add command "${normalized}"`
122+
};
90123
}
91124

92125
console.log(`Command "${normalized}" added successfully`);
93126

94127
// Fetch and register synonyms if enabled (default: true)
95128
const fetchSynonyms = options?.fetchSynonyms ?? true;
129+
let synonymsMapped: string[] = [];
96130

97131
if (fetchSynonyms) {
98-
// Fetch synonyms asynchronously (doesn't block command registration)
99-
this.fetchAndRegisterSynonyms(normalized).catch(error => {
132+
try {
133+
synonymsMapped = await this.fetchAndRegisterSynonyms(normalized);
134+
} catch (error) {
100135
console.error(`Error fetching synonyms for "${normalized}":`, error);
101-
});
136+
}
102137
}
103138

104-
return true;
139+
140+
return {
141+
success: true,
142+
commandName: normalized,
143+
synonymsMapped: synonymsMapped,
144+
synonymCount: synonymsMapped.length
145+
};
105146
}
106147

107148
/**
@@ -110,9 +151,9 @@ export class CommandMapping {
110151
*
111152
* @private
112153
* @param {string} commandName - The command name to fetch synonyms for
113-
* @returns {Promise<void>}
154+
* @returns {Promise<string[]>}
114155
*/
115-
private async fetchAndRegisterSynonyms(commandName: string): Promise<void> {
156+
private async fetchAndRegisterSynonyms(commandName: string): Promise<string[]>{
116157
console.log(`Fetching synonyms for "${commandName}"...`);
117158

118159
try {
@@ -121,18 +162,32 @@ export class CommandMapping {
121162

122163
if (synonyms.length === 0) {
123164
console.log(`No synonyms found for "${commandName}"`);
124-
return;
165+
return [];
125166
}
126167

127168
// Register all synonyms in the library
128-
const count = this.library.addSynonyms(synonyms, commandName);
169+
const successfullyAdded: string[] = [];
129170

130-
console.log(`Registered ${count} synonym(s) for "${commandName}": ${synonyms.slice(0, 5).join(', ')}${synonyms.length > 5 ? '...' : ''}`);
171+
for (const synonym of synonyms) {
172+
const added = this.library.addSynonym(synonym, commandName);
173+
if (added) {
174+
successfullyAdded.push(synonym);
175+
}
176+
}
177+
console.log(
178+
`Registered ${successfullyAdded.length} synonym(s) for "${commandName}": ` +
179+
`${successfullyAdded.slice(0, 5).join(', ')}${successfullyAdded.length > 5 ? '...' : ''}`
180+
);
181+
182+
return successfullyAdded;
131183
} catch (error) {
132184
console.error(`Failed to fetch synonyms for "${commandName}":`, error);
185+
return [];
133186
}
134187
}
135188

189+
190+
136191
/**
137192
* Manually adds a synonym for an existing command.
138193
* Use this to add custom synonyms that aren't in the API.
@@ -182,6 +237,37 @@ export class CommandMapping {
182237
return this.library.getSynonymsForCommand(commandName);
183238
}
184239

240+
/**
241+
* Gets a detailed mapping of all commands and their synonyms.
242+
* Useful for debugging or displaying to developers.
243+
*
244+
* @returns {Map<string, string[]>} Map of command names to their synonym arrays
245+
*
246+
* @example
247+
* ```ts
248+
* const mapping = mapper.getAllSynonymMappings();
249+
* for (const [command, synonyms] of mapping.entries()) {
250+
* console.log(`${command}: ${synonyms.join(', ')}`);
251+
* }
252+
* // Output:
253+
* // jump: leap, hop, spring, bound
254+
* // run: sprint, jog, dash
255+
* ```
256+
*/
257+
public getAllSynonymMappings(): Map<string, string[]> {
258+
const mappings = new Map<string, string[]>();
259+
const commands = this.library.list();
260+
261+
for (const command of commands) {
262+
const synonyms = this.library.getSynonymsForCommand(command.name);
263+
if (synonyms.length > 0) {
264+
mappings.set(command.name, synonyms);
265+
}
266+
}
267+
268+
return mappings;
269+
}
270+
185271
/**
186272
* Removes a command from the CommandLibrary by name.
187273
*

0 commit comments

Comments
 (0)