Skip to content

Conversation

vrushankportkey
Copy link
Collaborator

Description

Motivation

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)

How Has This Been Tested?

  • Unit Tests
  • Integration Tests
  • Manual Testing

Screenshots (if applicable)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Related Issues

Copy link
Contributor

Code Quality new feature maintainability security enhancement

Description

Summary By MatterAI MatterAI logo

🔄 What Changed

This pull request introduces two new default plugins: addPrefix and injectTools, and updates the .husky/pre-commit hook. The addPrefix plugin allows for prepending a specified string to AI completion or chat completion messages, supporting various content formats and roles. The injectTools plugin enables dynamic injection and management of tools for chat completion requests, including normalization, deduplication, and optional tool_choice presets. The pre-commit hook now includes an automatic Prettier formatting check and re-formatting, failing the commit if issues are found, ensuring consistent code style.

🔍 Impact of the Change

The addPrefix plugin enhances control over AI prompt engineering, allowing for consistent guardrails or system instructions to be applied programmatically. The injectTools plugin provides a flexible mechanism for integrating and managing external functionalities with AI models, improving their capabilities and reducing boilerplate. The updated pre-commit hook significantly boosts code quality and consistency across the codebase by enforcing formatting standards before commits, reducing merge conflicts and improving developer experience.

📁 Total Files Changed

  • .husky/pre-commit: Modified to add Prettier formatting check and auto-format on commit failure.
  • plugins/default/addPrefix.ts: New file adding a plugin to prefix AI completion/chat completion content.
  • plugins/default/injectTools.ts: New file adding a plugin to inject and manage tools for AI chat completion.
  • plugins/index.ts: Modified to register the new addPrefix and injectTools plugins.

🧪 Test Added

N/A

🔒Security Vulnerabilities

The new plugins include basic input validation for their respective parameters (prefix for addPrefix, tools for injectTools) and handle various data structures, which helps prevent common injection issues. No direct security vulnerabilities were detected in the added code.

Motivation

The motivation is to provide more robust and flexible mechanisms for interacting with AI models, specifically by allowing programmatic prefixing of prompts for guardrails or context, and by enabling dynamic injection and management of tools for enhanced AI capabilities. Additionally, the pre-commit hook aims to improve code quality and consistency.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)

How Has This Been Tested?

  • Unit Tests
  • Integration Tests
  • Manual Testing

Screenshots (if applicable)

N/A

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Related Issues

N/A

Tip

Quality Recommendations

  1. Add comprehensive unit tests for addPrefix.ts and injectTools.ts to cover all logic branches and edge cases, especially for content parsing and tool deduplication.

  2. Improve error logging in addPrefix.ts and injectTools.ts to include more context (e.g., context.requestType, parameters) when an error occurs, beyond just the message.

  3. Refine the pickKey function in dedupeTools to handle non-function tools more robustly than JSON.stringify, which can be order-dependent or slow for complex objects. Consider a canonical serialization or a more specific key for other tool types if they become more complex.

  4. Consider adding JSDoc comments to the exported functions and interfaces for better maintainability and IDE support.

Tanka Poem ♫

Code flows, new tools appear,
Prefixes guide the AI's ear.
Prettier's strict hand,
Formats the code, a clean command.
Logic shines, no fear. ✨

Sequence Diagram

sequenceDiagram
    participant Client as Client Application
    participant Gateway as API Gateway
    participant PluginHandler as Plugin Handler (addPrefix/injectTools)
    participant AIModel as AI Model

    Client->>Gateway: AI Request (e.g., POST /chat/completions)
    Note over Gateway: Intercepts request via 'beforeRequestHook'

    alt Request Type is 'chatComplete' or 'messages'
        Gateway->>+PluginHandler: handler(context, parameters, 'beforeRequestHook')
        Note over PluginHandler: Determines if addPrefix or injectTools is active

        alt addPrefix Plugin Active
            PluginHandler->>PluginHandler: getCurrentContentPart(context, eventType)
            PluginHandler->>PluginHandler: addPrefixToChatCompletion(context, prefix, applyToRole, addToExisting, onlyIfEmpty, eventType)
            Note over PluginHandler: Modifies 'messages' content with prefix
            PluginHandler->>PluginHandler: setCurrentContentPart(context, eventType, transformedData, updatedTexts)
        else injectTools Plugin Active
            PluginHandler->>PluginHandler: normalizeFunctionTool(tool: AnyTool)
            PluginHandler->>PluginHandler: dedupeTools(existing: AnyTool[], incoming: AnyTool[])
            Note over PluginHandler: Modifies 'tools' array and 'tool_choice'
        end

        PluginHandler-->>-Gateway: Transformed Request JSON
        Gateway->>AIModel: Forward Modified Request (with prefix/tools)
        AIModel-->>Gateway: AI Response
        Gateway-->>Client: Final Response
    else Other Request Type or Event Type
        Gateway-->>AIModel: Forward Original Request
        AIModel-->>Gateway: AI Response
        Gateway-->>Client: Final Response
    end

    alt Error in Plugin
        PluginHandler--xGateway: Error Object
        Gateway--xClient: Error Response
    end
Loading

Copy link
Contributor

@matter-code-review matter-code-review bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality is good overall with proper TypeScript types and error handling. Found several potential runtime bugs and areas for improvement.

Skipped files
  • plugins/default/manifest.json: Skipped file pattern

): Record<string, any> => {
const json = context.request.json;
const updatedJson = { ...json };
const messages = Array.isArray(json.messages) ? [...json.messages] : [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Bug Fix

Issue: Potential null pointer exception when json.messages is null/undefined
Fix: Add null check before array operations
Impact: Prevents runtime crashes when messages property is missing

Suggested change
const messages = Array.isArray(json.messages) ? [...json.messages] : [];
const messages = Array.isArray(json?.messages) ? [...json.messages] : [];

Comment on lines +68 to +70
const isEmptyContent =
(typeof content === 'string' && content.trim().length === 0) ||
(Array.isArray(content) && content.length === 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Bug Fix

Issue: Logical OR operator creates incorrect boolean condition - will always be true
Fix: Use logical AND operator for proper empty content detection
Impact: Fixes empty content detection logic to work correctly

Suggested change
const isEmptyContent =
(typeof content === 'string' && content.trim().length === 0) ||
(Array.isArray(content) && content.length === 0);
const isEmptyContent =
(typeof content === 'string' && content.trim().length === 0) ||
(Array.isArray(content) && content.length === 0) ||
content == null;

Comment on lines +114 to +116
const existingTools: AnyTool[] = Array.isArray(originalJson.tools)
? [...originalJson.tools]
: [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Bug Fix

Issue: Potential null pointer exception when accessing originalJson.tools
Fix: Add null check before array operations
Impact: Prevents runtime crashes when tools property is missing

Suggested change
const existingTools: AnyTool[] = Array.isArray(originalJson.tools)
? [...originalJson.tools]
: [];
const existingTools: AnyTool[] = Array.isArray(originalJson?.tools)
? [...originalJson.tools]
: [];

Comment on lines +140 to +142
const hasExistingToolChoice =
(originalJson as Record<string, unknown>)?.tool_choice !== undefined &&
(originalJson as Record<string, unknown>)?.tool_choice !== null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Code Refactor

Issue: Complex type assertions make code hard to read and maintain
Fix: Extract type definitions for better readability
Impact: Improves code maintainability and type safety

Suggested change
const hasExistingToolChoice =
(originalJson as Record<string, unknown>)?.tool_choice !== undefined &&
(originalJson as Record<string, unknown>)?.tool_choice !== null;
type JsonWithToolChoice = Record<string, unknown> & {
tool_choice?: unknown;
};
const hasExistingToolChoice =
(originalJson as JsonWithToolChoice)?.tool_choice !== undefined &&
(originalJson as JsonWithToolChoice)?.tool_choice !== null;

Comment on lines +154 to +161
const exists = nextTools.some(
(t) =>
t &&
(t as { type?: unknown; function?: { name?: unknown } }).type ===
'function' &&
(t as { function?: { name?: unknown } }).function?.name ===
requiredFunctionName
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Code Refactor

Issue: Deeply nested type assertions are hard to read and error-prone
Fix: Extract type guard function for tool validation
Impact: Improves code readability and reduces type assertion complexity

Suggested change
const exists = nextTools.some(
(t) =>
t &&
(t as { type?: unknown; function?: { name?: unknown } }).type ===
'function' &&
(t as { function?: { name?: unknown } }).function?.name ===
requiredFunctionName
);
const isValidFunctionTool = (t: AnyTool): boolean => {
return t?.type === 'function' &&
typeof t.function === 'object' &&
t.function?.name === requiredFunctionName;
};
const exists = nextTools.some(isValidFunctionTool);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant