All files / src/llm-orchestration/action-handlers howto.handler.ts

78.94% Statements 15/19
50% Branches 2/4
60% Functions 3/5
76.47% Lines 13/17

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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 1336x             6x 6x 6x 6x 6x               6x 6x                                       10x           2x                 2x                                                                     2x                                               2x                                    
import { BadRequestException, Injectable } from '@nestjs/common';
import { ActionHandler } from './action-handler.interface';
import {
  ActionExecutionResult,
  PlanExecutionContext,
  ToolMetadata,
} from '../llm-orchestration.interfaces';
import { generateToolCall, generateToolCallJson } from '../../utils';
import { HowtoArgsDto } from './dto/howto.args.dto';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import {
  ACTION_ITEM_END_TAG,
  ACTION_ITEM_START_TAG,
  FIELD_TOOL_NAME,
  SPECIAL_FIELD_TOKEN,
} from '../parser/parsing.constants';
 
@Injectable()
export class HowtoHandler implements ActionHandler {
  readonly toolName = 'howto';
 
  getMetadata(): ToolMetadata {
    return {
      name: this.toolName,
      description: this.getDefinition(true),
      arguments: [], // This tool has no structured arguments for execution, it's a meta-tool.
    };
  }
 
  /**
   * Generates a tool call example in the specified format.
   * @param toolCall - The tool call object to format
   * @param useJson - If true, uses JSON format; otherwise uses XML-style format
   * @returns Formatted tool call string
   */
  private generateExample(
    toolCall: Record<string, any>,
    useJson: boolean = false,
  ): string {
    return useJson
      ? generateToolCallJson(toolCall)
      : generateToolCall(toolCall);
  }
 
  getDefinition(useJsonFormat: boolean = false): string {
    const singleToolExample = this.generateExample(
      {
        tool_name: 'do_something',
        target_file: 'src/components/Component.tsx',
        reason: 'Refactoring component to use new API.',
      },
      useJsonFormat,
    );
 
    const batchExample = [
      this.generateExample(
        {
          tool_name: 'tool1',
          argA: 'value for tool 1',
          argB: 'another value',
        },
        useJsonFormat,
      ),
      this.generateExample(
        {
          tool_name: 'tool2',
          target_path: 'src/modules/some-feature/file.ts',
          mode: 'read-only',
        },
        useJsonFormat,
      ),
      this.generateExample(
        {
          tool_name: 'tool3',
          config_option: 'enable_feature_x',
          value: true,
        },
        useJsonFormat,
      ),
      this.generateExample(
        {
          tool_name: 'tool1',
          argA: 'a different value for tool 1',
          argB: 'yet another value',
        },
        useJsonFormat,
      ),
    ].join('\n---\n');
 
    const definition = `
-------------
### ${this.toolName}
  This is a guide on how to structure tool calls. It is not an executable tool itself.
 
  Each action must be wrapped in \`${ACTION_ITEM_START_TAG}\` and \`${ACTION_ITEM_END_TAG}\` tags.
  Fields are defined using \`${SPECIAL_FIELD_TOKEN}\` delimiter (e.g., \`${SPECIAL_FIELD_TOKEN}field_name${SPECIAL_FIELD_TOKEN} value\`).
  The \`${SPECIAL_FIELD_TOKEN}${FIELD_TOOL_NAME}${SPECIAL_FIELD_TOKEN}\` field is mandatory for all tools. Other arguments are tool-specific.
 
  **CRITICAL RULE**: Never ever use a tool that is not explicitly available in the system instructions.
 
  #### Single Tool Call
 
  Each tool call must be enclosed in its own action item block. The tool name is mandatory, followed by its arguments.
 
:${singleToolExample}
 
  #### Parallel (Batch) Tool Calls
 
  To execute multiple tools in parallel, define multiple action items back-to-back, separated by '---'. The system will execute compatible, non-conflicting actions concurrently.
 
:${batchExample}
-------------
`;
    return `\n${definition.trim()}\n`;
  }
 
  async execute(
    args: { [key: string]: any },
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    _context: PlanExecutionContext,
  ): Promise<ActionExecutionResult> {
    // Validate to maintain structural consistency, though it will always pass.
    const validatedArgs = plainToClass(HowtoArgsDto, args);
    await validate(validatedArgs);
 
    // This tool is for documentation only and should never be executed.
    throw new BadRequestException(
      `'${this.toolName}' is a documentation placeholder and not an executable tool.`,
    );
  }
}