Skip to content

Conversation

@dittops
Copy link
Contributor

@dittops dittops commented Aug 27, 2025

Fixes #251

Adds frontend validation to prevent users from creating adapters with duplicate names within the same endpoint.

Changes

  • Added case-insensitive duplicate name validation in AdapterDetail component
  • Fetches existing adapters to validate against
  • Shows clear error message for duplicate names
  • Prevents form submission with invalid names

Generated with Claude Code

- Add useEndPoints hook to fetch existing adapters
- Implement validateAdapterName function for case-insensitive duplicate checking
- Update TextInput rules to include duplicate validation
- Prevents users from creating adapters with same names within an endpoint

Fixes #251

Co-authored-by: Ditto P S <[email protected]>
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @dittops, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces frontend validation to prevent users from creating new adapters with names that already exist within the same endpoint. This enhancement ensures data integrity and improves user experience by providing immediate feedback on duplicate entries.

Highlights

  • Duplicate Name Validation: Implemented case-insensitive validation for adapter names within the AdapterDetail component.
  • Existing Adapter Fetching: The system now fetches existing adapters to perform accurate validation against current entries.
  • User Feedback: A clear error message is displayed to the user when a duplicate adapter name is detected.
  • Form Submission Prevention: The form is prevented from being submitted if an invalid (duplicate) adapter name is entered.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dittops
Copy link
Contributor Author

dittops commented Aug 27, 2025

@claude The validation was done on the frontend, which is not correct. You should do this on the backend/budapp properly.

Copy link
Contributor

@gemini-code-assist gemini-code-assist 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 Review

This pull request adds frontend validation to prevent creating adapters with duplicate names, which is a great improvement. The implementation correctly uses a custom validator with antd Form.

My review includes two main points:

  1. A potential issue where not all existing adapters are fetched for validation due to a hardcoded limit, which could allow duplicates if there are more than 100 adapters.
  2. A suggestion to optimize the validation function using useCallback and add type annotations for better performance and maintainability.

Addressing these points will make the validation more robust and the code more efficient.

Comment on lines 38 to 47
useEffect(() => {
if (currentWorkflow?.workflow_steps?.adapter_config?.endpoint_id) {
const endpointId = currentWorkflow.workflow_steps.adapter_config.endpoint_id;
getAdapters({
endpointId: endpointId,
page: 1,
limit: 100, // Get all adapters for validation
}, projectId);
}
}, [currentWorkflow?.workflow_steps?.adapter_config?.endpoint_id, projectId])
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The useEffect hook for fetching adapters has a hardcoded limit of 100. This could lead to incorrect duplicate name validation if there are more than 100 adapters for an endpoint, as not all existing adapters would be fetched. A more robust solution would be to implement pagination to retrieve all adapters.

Additionally, the getAdapters function should be included in the useEffect dependency array to prevent potential stale closures and adhere to the exhaustive-deps linting rule.

Suggested change
useEffect(() => {
if (currentWorkflow?.workflow_steps?.adapter_config?.endpoint_id) {
const endpointId = currentWorkflow.workflow_steps.adapter_config.endpoint_id;
getAdapters({
endpointId: endpointId,
page: 1,
limit: 100, // Get all adapters for validation
}, projectId);
}
}, [currentWorkflow?.workflow_steps?.adapter_config?.endpoint_id, projectId])
useEffect(() => {
if (currentWorkflow?.workflow_steps?.adapter_config?.endpoint_id) {
const endpointId = currentWorkflow.workflow_steps.adapter_config.endpoint_id;
getAdapters({
endpointId: endpointId,
page: 1,
limit: 1000, // TODO: Fetch all adapters for complete validation. This is a temporary higher limit.
}, projectId);
}
}, [currentWorkflow?.workflow_steps?.adapter_config?.endpoint_id, projectId, getAdapters])

Comment on lines 50 to 65
const validateAdapterName = (_, value) => {
if (!value) {
return Promise.resolve();
}

// Check if the name already exists (case-insensitive)
const existingAdapter = adapters.find(adapter =>
adapter.name.toLowerCase() === value.toLowerCase()
);

if (existingAdapter) {
return Promise.reject(new Error('An adapter with this name already exists'));
}

return Promise.resolve();
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The validateAdapterName function is redefined on every render. For better performance, it should be memoized using useCallback. Also, adding type annotations for the function parameters would improve type safety and code clarity.

Note: You'll need to import useCallback from react.

Suggested change
const validateAdapterName = (_, value) => {
if (!value) {
return Promise.resolve();
}
// Check if the name already exists (case-insensitive)
const existingAdapter = adapters.find(adapter =>
adapter.name.toLowerCase() === value.toLowerCase()
);
if (existingAdapter) {
return Promise.reject(new Error('An adapter with this name already exists'));
}
return Promise.resolve();
};
// Validation function to check for duplicate names
const validateAdapterName = useCallback((_: any, value: string) => {
if (!value) {
return Promise.resolve();
}
// Check if the name already exists (case-insensitive)
const existingAdapter = adapters.find(adapter =>
adapter.name.toLowerCase() === value.toLowerCase()
);
if (existingAdapter) {
return Promise.reject(new Error('An adapter with this name already exists'));
}
return Promise.resolve();
}, [adapters]);

@claude
Copy link
Contributor

claude bot commented Aug 27, 2025

Claude finished @dittops's task —— View job


I've successfully moved the adapter name validation from frontend to backend as requested.

Todo List

  • Read current frontend validation logic
  • Find budapp adapter creation endpoint
  • Implement backend validation for duplicate adapter names
  • Update frontend to remove validation and handle backend errors
  • Test and lint changes
  • Commit and push changes

Changes Made

  1. Backend: Updated services/budapp/budapp/endpoint_ops/services.py to use case-insensitive validation by adding case_sensitive=False parameter to the adapter name check
  2. Backend: Updated error message to match frontend: "An adapter with this name already exists"
  3. Frontend: Removed duplicate frontend validation logic from AdapterDetail.tsx component
  4. Frontend: Removed unused imports and adapter fetching code from frontend
  5. Frontend: Removed custom validator from form rules

Summary

The validation is now properly handled on the backend where it belongs. The backend performs case-insensitive duplicate name checking and returns a 400 error with the message "An adapter with this name already exists" when a duplicate is detected. The frontend will display this error message through its existing error handling mechanism, providing a better separation of concerns.

The changes have been committed and pushed to the branch: claude/issue-251-20250827-0616

- Updated backend validation in budapp to use case-insensitive comparison
- Added case_sensitive=False parameter to AdapterDataManager.retrieve_by_fields call
- Updated error message to be consistent with frontend expectations
- Removed frontend validation logic from AdapterDetail.tsx component
- Removed unused imports and adapter fetching code from frontend

Co-authored-by: Ditto P S <[email protected]>
@dittops
Copy link
Contributor Author

dittops commented Aug 28, 2025

@codex review this pr

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

Codex Review: Here are some suggestions.

Reply with @codex fix comments to fix any unresolved comments.

About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".

Comment on lines 1615 to 1620
{"name": adapter_name, "endpoint_id": endpoint_id},
missing_ok=True,
exclude_fields={"status": AdapterStatusEnum.DELETED},
case_sensitive=False,
)
if db_adapters:

Choose a reason for hiding this comment

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

[P1] Handle multiple case-insensitive matches gracefully

The new call to retrieve_by_fields(..., case_sensitive=False) now collapses name comparison to lowercase, but retrieve_by_fields still uses scalar_one_or_none. If the database already contains two adapters whose names differ only by casing (e.g. "Adapter" and "adapter", which was previously allowed because validation was case-sensitive), this query will now raise MultipleResultsFound and bubble up as a 500 instead of returning a ClientException. The change prevents new duplicates, but it makes the API fail for existing data rather than surfacing a user-facing validation error. Consider retrieving all matches or constraining the query to a single row before raising the custom exception.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@claude what's your take on this?

Copy link
Contributor

@claude claude bot Aug 28, 2025

Choose a reason for hiding this comment

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

Claude finished @dittops's task —— View job


Analyzing the Codex review concern about case-insensitive validation...

Todo List

  • Read the current implementation in services/budapp/budapp/endpoint_ops/services.py
  • Understand how retrieve_by_fields works with case_sensitive=False
  • Assess the validity of the MultipleResultsFound concern
  • Provide recommendations for handling the edge case

Analysis and Assessment

Codex is absolutely correct about this concern. Here's the detailed analysis:

The Problem

  1. Current Behavior: The code uses retrieve_by_fields(..., case_sensitive=False) which calls scalar_one_or_none() on the database query.

  2. SQLAlchemy's scalar_one_or_none() Behavior: This method will raise sqlalchemy.exc.MultipleResultsFound if the query returns more than one row.

  3. The Edge Case: If the database already contains adapters with names that differ only by case (e.g., "Adapter" and "adapter"), the case-insensitive query func.lower(field) == func.lower(value) would match both records.

  4. Current Problem: Instead of getting a user-friendly validation error, users would get a 500 error with MultipleResultsFound exception.

Database Schema Analysis

Looking at the adapter table creation in migration 35c352d33c91_adapter_table_workflow_type.py, I can see:

  • No unique constraint on the (name, endpoint_id) combination
  • No database-level case-insensitive constraint
  • The previous case-sensitive validation allowed "Adapter" and "adapter" to coexist in the same endpoint

The Risk

This is a real production risk because:

  1. The previous frontend-only validation was case-sensitive
  2. Users could have created adapters with names differing only in case
  3. The new backend validation would crash with MultipleResultsFound instead of providing helpful validation

Recommendation

Option 1: Robust Query Approach (Recommended)
Replace the current logic with:

# Use scalars_all() to get all matching adapters
db_adapters = await AdapterDataManager(self.session).scalars_all(
    select(AdapterModel).filter(
        func.lower(AdapterModel.name) == func.lower(adapter_name),
        AdapterModel.endpoint_id == endpoint_id,
        AdapterModel.status != AdapterStatusEnum.DELETED
    )
)
if db_adapters:
    raise ClientException("An adapter with this name already exists")

Option 2: Try-Catch Approach
Wrap the current code in try-catch to handle MultipleResultsFound:

try:
    db_adapters = await AdapterDataManager(self.session).retrieve_by_fields(...)
    if db_adapters:
        raise ClientException("An adapter with this name already exists")
except MultipleResultsFound:
    raise ClientException("An adapter with this name already exists")

I recommend Option 1 as it's cleaner and more explicit about the intention.


@dittops
Copy link
Contributor Author

dittops commented Aug 28, 2025

@claude the issue reported is not casesensitve names. even though the name used previously, when tried to use the same name again, it accepted and started the lora deployment process.. Think harder and fix this

@claude
Copy link
Contributor

claude bot commented Aug 28, 2025

Claude finished @dittops's task —— View job


I've successfully identified and fixed the adapter name validation bug!

Todo List

  • Read and analyze current adapter validation logic in budapp
  • Identify the exact issue with validation logic
  • Fix the validation logic to properly prevent duplicates
  • Test and lint changes
  • Commit and push the fix

Root Cause Found! 🎯

I've identified two critical issues in the current validation logic:

Issue #1: Validation Bypass Bug (Main Issue)

The validation logic at lines 1612-1621 ran only once when a workflow step was first created. However, if a user retried the adapter creation (which reused the same workflow), the code followed this path:

  1. First attempt: Validation runs → passed → workflow step created
  2. Retry/Second attempt: Workflow step exists → goes to line 1659 (if db_current_workflow_step:) → validation was completely bypassed → adapter creation proceeded

This explained why "even though the name used previously, when tried to use the same name again, it accepted and started the lora deployment process."

Issue #2: MultipleResultsFound Exception Risk

The previous validation used retrieve_by_fields() with scalar_one_or_none(), which would throw a 500 error if multiple case-variants existed in the database.

Solution Implemented ✅

I've fixed both issues by:

  1. Created a proper validation helper method (_validate_adapter_name) that uses scalars_all() to safely handle multiple results
  2. Moved validation to run on every call - validation now runs in both the "existing workflow step" and "new workflow step" branches
  3. Fixed the MultipleResultsFound risk by using direct SQLAlchemy queries with proper case-insensitive matching

Changes Made

  • Added imports: SQLAlchemy select, func, cast, String for proper case-insensitive queries
  • Created helper method: _validate_adapter_name() that safely validates against existing adapters
  • Fixed validation placement: Validation now runs on every call, not just first workflow creation
  • Improved error handling: Uses scalars_all() to gracefully handle multiple database results

The validation now properly prevents duplicate adapter names on every attempt, not just the first one!

Summary

The issue was a logic bug where adapter name validation only ran once during workflow creation, but was bypassed on subsequent retries. The fix ensures validation runs consistently on every adapter creation attempt.


| Branch: claude/issue-251-20250827-0616

- Fix validation bypass bug where duplicate names were accepted on retry attempts
- Move validation from one-time execution to every workflow call
- Add proper _validate_adapter_name helper method using scalars_all()
- Prevent MultipleResultsFound exceptions from case-insensitive queries
- Ensure validation runs in both existing and new workflow step branches

Co-authored-by: Ditto P S <[email protected]>
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.

FE::User is able to give same name for different adapters

2 participants