All files / src/app/extensions/copilot-embedded/models/copilot-embedded-choice copilot-embedded-choice.mapper.ts

100% Statements 13/13
92.85% Branches 13/14
100% Functions 5/5
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44                8x       8x 19x     8x                 18x 18x       8x 8x 1x     7x 7x              
import {
  CopilotEmbeddedToolCall,
  CopilotEmbeddedToolName,
} from '../copilot-embedded-tool-call/copilot-embedded-tool-call.model';
 
import { CopilotEmbeddedChoiceData } from './copilot-embedded-choice.interface';
import { CopilotEmbeddedChoice } from './copilot-embedded-choice.model';
 
const CHOICE_TOOL: CopilotEmbeddedToolName = 'PWA_ask_choice';
 
/** Accepts either a real array or a pipe-separated string (the resilient Flowise schema shape). */
function parseOptions(raw: unknown): string[] {
  const values = Array.isArray(raw) ? raw : typeof raw === 'string' ? raw.split('|') : [];
  return values.map(option => `${option}`.trim()).filter(Boolean);
}
 
export class CopilotEmbeddedChoiceMapper {
  /**
   * Maps the `PWA_ask_choice` signal tool call of a chatflow response to a choice prompt, so the PWA
   * can render the options as clickable choice chips.
   *
   * @param tools The tool calls of a chatflow response (`usedTools`).
   * @returns     The choice prompt or `undefined` when the tool was not called or carries no usable options.
   */
  static fromToolCalls(tools: CopilotEmbeddedToolCall[]): CopilotEmbeddedChoice {
    const call = tools?.find(tool => tool?.tool === CHOICE_TOOL);
    return call ? CopilotEmbeddedChoiceMapper.fromData(call.toolInput as CopilotEmbeddedChoiceData) : undefined;
  }
 
  private static fromData(data: CopilotEmbeddedChoiceData): CopilotEmbeddedChoice {
    const options = parseOptions(data?.options);
    if (!options.length) {
      return;
    }
 
    const question = typeof data.question === 'string' ? data.question.trim() : undefined;
    return {
      question: question || undefined,
      options,
      multiSelect: data.multiSelect === true || data.multiSelect === 'true',
    };
  }
}