diff --git a/AGENTS.md b/AGENTS.md index 782861ad36caf7..d62e6ee49ad942 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,54 +1,253 @@ -# AGENTS.md +# Compliance Rules -## Project Overview +This file contains the compliance and code quality rules for this repository. -Dify is an open-source platform for developing LLM applications with an intuitive interface combining agentic AI workflows, RAG pipelines, agent capabilities, and model management. +## 1. Python Functions Must Include Type Annotations -The codebase is split into: +**Objective:** Ensure type safety and improve code maintainability by requiring explicit type annotations for all function parameters and return values in Python code, as enforced by basedpyright type checking -- **Backend API** (`/api`): Python Flask application organized with Domain-Driven Design -- **Frontend Web** (`/web`): Next.js 15 application using TypeScript and React 19 -- **Docker deployment** (`/docker`): Containerized deployment configurations +**Success Criteria:** All Python function definitions include type hints for parameters and return types using Python 3.12+ syntax (e.g., list[str] instead of List[str], int | None instead of Optional[int]) -## Backend Workflow +**Failure Criteria:** Python function definitions lack type annotations for parameters or return values -- Run backend CLI commands through `uv run --project api `. +--- -- Before submission, all backend modifications must pass local checks: `make lint`, `make type-check`, and `uv run --project api --dev dev/pytest/pytest_unit_tests.sh`. +## 2. Python Code Must Follow Ruff Linting Rules -- Use Makefile targets for linting and formatting; `make lint` and `make type-check` cover the required checks. +**Objective:** Maintain consistent code quality and style by adhering to the project's ruff configuration, which enforces rules for code formatting, import ordering, security checks, and best practices -- Integration tests are CI-only and are not expected to run in the local environment. +**Success Criteria:** All Python code passes ruff format and ruff check --fix without errors, following the rules defined in .ruff.toml including line length of 120 characters, proper import sorting, and security rules (S102, S307, S301, S302, S311) -## Frontend Workflow +**Failure Criteria:** Python code violates ruff linting rules such as improper formatting, incorrect import ordering, use of print() instead of logging, or security violations like using exec/eval/pickle -```bash -cd web -pnpm lint:fix -pnpm type-check:tsgo -pnpm test -``` +--- -## Testing & Quality Practices +## 3. Backend Code Must Use Logging Instead of Print Statements -- Follow TDD: red → green → refactor. -- Use `pytest` for backend tests with Arrange-Act-Assert structure. -- Enforce strong typing; avoid `Any` and prefer explicit type annotations. -- Write self-documenting code; only add comments that explain intent. +**Objective:** Enable proper observability and debugging by requiring all output to use the logging module rather than print statements, with logger instances declared at module level -## Language Style +**Success Criteria:** All logging is performed using logger = logging.getLogger(__name__) declared at module top, with no print() statements in production code (tests are exempt) -- **Python**: Keep type hints on functions and attributes, and implement relevant special methods (e.g., `__repr__`, `__str__`). -- **TypeScript**: Use the strict config, rely on ESLint (`pnpm lint:fix` preferred) plus `pnpm type-check:tsgo`, and avoid `any` types. +**Failure Criteria:** Code contains print() statements outside of test files, or logging is performed without proper logger initialization -## General Practices +--- -- Prefer editing existing files; add new documentation only when requested. -- Inject dependencies through constructors and preserve clean architecture boundaries. -- Handle errors with domain-specific exceptions at the correct layer. +## 4. Python Code Must Use Modern Type Syntax for Python 3.12+ -## Project Conventions +**Objective:** Leverage modern Python type system features for better code clarity and type safety by using the latest type annotation syntax -- Backend architecture adheres to DDD and Clean Architecture principles. -- Async work runs through Celery with Redis as the broker. -- Frontend user-facing strings must use `web/i18n/en-US/`; avoid hardcoded text. +**Success Criteria:** Type annotations use Python 3.12+ syntax: list[T] instead of List[T], dict[K,V] instead of Dict[K,V], int | None instead of Optional[int], and str | int instead of Union[str, int] + +**Failure Criteria:** Code uses legacy typing imports like List, Dict, Optional, Union from the typing module when modern syntax is available + +--- + +## 5. Python Backend Files Must Not Exceed 800 Lines + +**Objective:** Maintain code readability and modularity by keeping individual Python files under 800 lines, promoting proper code organization and separation of concerns + +**Success Criteria:** All Python files in the backend (api/) contain fewer than 800 lines of code + +**Failure Criteria:** Python files in the backend exceed 800 lines and should be split into multiple files + +--- + +## 6. SQLAlchemy Sessions Must Use Context Managers + +**Objective:** Ensure proper database connection management and prevent resource leaks by requiring all SQLAlchemy sessions to be opened with context managers + +**Success Criteria:** All database operations use 'with Session(db.engine, expire_on_commit=False) as session:' pattern for session management + +**Failure Criteria:** Database sessions are created without context managers or sessions are not properly closed + +--- + +## 7. Database Queries Must Include tenant_id Scoping + +**Objective:** Ensure data isolation and security in multi-tenant architecture by requiring all database queries to be scoped by tenant_id to prevent cross-tenant data access + +**Success Criteria:** All database queries that access tenant-scoped resources include WHERE clauses filtering by tenant_id + +**Failure Criteria:** Database queries access tenant-scoped tables without tenant_id filtering, creating potential data leakage + +--- + +## 8. Python Tests Must Follow pytest AAA Pattern + +**Objective:** Maintain clear and maintainable test structure by requiring all pytest tests to follow the Arrange-Act-Assert pattern for better readability and understanding + +**Success Criteria:** Test functions are structured with three distinct sections: Arrange (setup), Act (execution), Assert (verification), with clear separation between phases + +**Failure Criteria:** Test functions mix setup, execution, and assertion logic without clear separation or organization + +--- + +## 9. TypeScript Must Avoid any Type Annotations + +**Objective:** Maintain type safety in the frontend codebase by avoiding the any type, which bypasses TypeScript's type checking and can lead to runtime errors + +**Success Criteria:** TypeScript code uses specific types or unknown instead of any, with ts/no-explicit-any warnings addressed + +**Failure Criteria:** Code contains 'any' type annotations without justified exceptions or proper type definitions + +--- + +## 10. TypeScript Must Use Type Definitions Instead of Interfaces + +**Objective:** Maintain consistency in type declarations across the codebase by preferring type definitions over interfaces, as enforced by ts/consistent-type-definitions rule + +**Success Criteria:** All TypeScript type declarations use 'type' keyword instead of 'interface' keyword, following the pattern: type MyType = { ... } + +**Failure Criteria:** Code uses 'interface' declarations instead of 'type' definitions + +--- + +## 11. Frontend User-Facing Strings Must Use i18n Translations + +**Objective:** Enable internationalization and localization by requiring all user-facing text in the frontend to be retrieved from i18n translation files rather than hardcoded + +**Success Criteria:** All user-facing strings are defined in web/i18n/en-US/ translation files and accessed via useTranslation hook with proper namespace options, following dify-i18n/require-ns-option rule + +**Failure Criteria:** User-facing strings are hardcoded in component files instead of using translation keys + +--- + +## 12. TypeScript Files Must Follow Strict TypeScript Configuration + +**Objective:** Ensure type safety and catch potential errors at compile time by enabling strict TypeScript compiler options + +**Success Criteria:** All TypeScript code compiles successfully with strict mode enabled in tsconfig.json, including strict type checking and consistent casing enforcement + +**Failure Criteria:** Code contains type errors or inconsistent casing that would fail strict TypeScript compilation + +--- + +## 13. Backend Configuration Must Be Accessed via configs Module + +**Objective:** Centralize configuration management and prevent direct environment variable access by requiring all configuration to be retrieved through the configs module + +**Success Criteria:** Configuration values are accessed through configs.dify_config or related config modules, not via direct os.environ or os.getenv calls + +**Failure Criteria:** Code directly reads environment variables using os.environ or os.getenv instead of using the configs module + +--- + +## 14. Python Backend Must Use Pydantic v2 for Data Validation + +**Objective:** Ensure consistent data validation and serialization using Pydantic v2 models with proper configuration for DTOs and request/response validation + +**Success Criteria:** All data transfer objects use Pydantic v2 BaseModel with ConfigDict(extra='forbid') by default, and use @field_validator/@model_validator for domain rules + +**Failure Criteria:** Data validation uses Pydantic v1 syntax, allows undeclared fields without explicit configuration, or uses custom validation logic instead of Pydantic validators + +--- + +## 15. Backend Errors Must Use Domain-Specific Exceptions + +**Objective:** Provide clear error handling and appropriate HTTP responses by raising domain-specific exceptions from services and translating them to HTTP responses in controllers + +**Success Criteria:** Business logic raises exceptions from services/errors or core/errors modules, and controllers handle these exceptions to return appropriate HTTP responses + +**Failure Criteria:** Services return HTTP responses directly, or generic exceptions are raised without domain context + +--- + +## 16. Python Code Must Use Snake Case for Variables and Functions + +**Objective:** Maintain consistent naming conventions across the Python codebase by using snake_case for variables and functions, PascalCase for classes, and UPPER_CASE for constants + +**Success Criteria:** All Python variables and functions use snake_case naming (e.g., user_name, get_user_data), classes use PascalCase (e.g., UserService), and constants use UPPER_CASE (e.g., MAX_RETRIES) + +**Failure Criteria:** Python code uses camelCase, PascalCase for variables/functions, or inconsistent naming patterns + +--- + +## 17. Frontend ESLint Sonarjs Rules Must Be Followed + +**Objective:** Maintain code quality and prevent common bugs by adhering to SonarJS cognitive complexity and maintainability rules configured in the project + +**Success Criteria:** TypeScript code passes SonarJS linting rules including no-dead-store (error level), max-lines warnings (1000 line limit), and no-variable-usage-before-declaration (error level) + +**Failure Criteria:** Code violates SonarJS rules such as dead stores, files exceeding 1000 lines, or variables used before declaration + +--- + +## 18. Backend Architecture Must Follow Import Layer Constraints + +**Objective:** Maintain clean architecture boundaries by enforcing layer separation through import-linter rules that prevent circular dependencies and upward imports + +**Success Criteria:** Code adheres to import-linter contracts defined in .importlinter, including workflow layer separation (graph_engine → graph → nodes → entities) and domain isolation rules + +**Failure Criteria:** Imports violate architectural layers by importing from higher layers or creating circular dependencies not explicitly allowed in .importlinter + +--- + +## 19. Backend Storage Access Must Use Abstraction Layer + +**Objective:** Ensure consistent and secure storage operations by requiring all storage access to go through extensions.ext_storage.storage abstraction + +**Success Criteria:** All file storage operations use extensions.ext_storage.storage interface instead of direct filesystem or cloud storage APIs + +**Failure Criteria:** Code directly accesses filesystem, S3, Azure Blob, or other storage without using the storage abstraction layer + +--- + +## 20. Backend HTTP Requests Must Use SSRF Proxy Helper + +**Objective:** Prevent Server-Side Request Forgery attacks by requiring all outbound HTTP requests to use the SSRF proxy helper for validation and protection + +**Success Criteria:** All outbound HTTP fetches use core.helper.ssrf_proxy instead of direct httpx, requests, or urllib calls + +**Failure Criteria:** Code makes outbound HTTP requests without using the SSRF proxy helper, potentially exposing internal resources + +--- + +## 21. Python Code Must Not Override Dunder Methods Unnecessarily + +**Objective:** Prevent subtle bugs and maintain expected Python object behavior by avoiding unnecessary overrides of special methods like __init__, __iadd__, etc. + +**Success Criteria:** Special methods (__init__, __iadd__, __str__, __repr__) are only overridden when necessary with proper implementation of relevant special methods documented in coding_style.md + +**Failure Criteria:** Code overrides dunder methods without clear justification or without implementing complementary methods + +--- + +## 22. Backend Must Avoid Security-Risky Functions + +**Objective:** Prevent remote code execution vulnerabilities by prohibiting the use of dangerous built-in functions that can execute arbitrary code + +**Success Criteria:** Python code does not use exec(), eval(), pickle, marshal, or ast.literal_eval() as enforced by ruff security rules S102, S307, S301, S302 + +**Failure Criteria:** Code uses exec(), eval(), pickle.loads(), marshal.loads(), or ast.literal_eval() which can execute arbitrary code + +--- + +## 23. Python Backend Must Use Deterministic Control Flow + +**Objective:** Optimize for observability and debugging by maintaining deterministic control flow with clear logging and actionable errors + +**Success Criteria:** Code avoids clever hacks, maintains readable control flow, includes tenant/app/workflow identifiers in log context, and logs retryable events at warning level and terminal failures at error level + +**Failure Criteria:** Code uses obfuscated logic, lacks proper logging context, or mixes logging levels inappropriately + +--- + +## 24. Backend Async Tasks Must Be Idempotent + +**Objective:** Ensure reliability of background processing by requiring all async tasks to be idempotent and log relevant object identifiers for debugging + +**Success Criteria:** All background tasks in tasks/ are idempotent (can be safely retried), log the relevant object identifiers (tenant_id, app_id, etc.), and specify explicit queue selection + +**Failure Criteria:** Background tasks are not idempotent, lack proper logging identifiers, or don't specify queue configuration + +--- + +## 25. Frontend Code Must Not Use console Statements + +**Objective:** Prevent debug code from reaching production by treating console statements as warnings that should be removed or replaced with proper logging + +**Success Criteria:** Production frontend code avoids console.log, console.warn, console.error statements as enforced by no-console warning rule + +**Failure Criteria:** Code contains console statements that should be removed or replaced with proper logging mechanisms + +--- diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index 5ffa87375c9b4d..1a8101ae9ddc16 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -21,7 +21,6 @@ import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/ import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import { ChangeType, InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' import ConfigContext from '@/context/debug-configuration' import { AppModeEnum, TransferMethod } from '@/types/app' import { checkKeys, getNewVarInWorkflow, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' @@ -109,7 +108,7 @@ const ConfigModal: FC = ({ }, [t]) const handlePayloadChange = useCallback((key: string) => { return (value: any) => { - setTempPayload((prev) => { + setTempPayload((prev: any) => { const newPayload = { ...prev, [key]: value, @@ -198,8 +197,6 @@ const ConfigModal: FC = ({ if (type === InputVarType.multiFiles) draft.max_length = DEFAULT_FILE_UPLOAD_SETTING.max_length } - if (type === InputVarType.paragraph) - draft.max_length = DEFAULT_VALUE_MAX_LEN }) setTempPayload(newPayload) }, [tempPayload]) diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index 4a38fc92a6a4d7..1a8810f7cd883a 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -15,7 +15,6 @@ import Confirm from '@/app/components/base/confirm' import Toast from '@/app/components/base/toast' import Tooltip from '@/app/components/base/tooltip' import { InputVarType } from '@/app/components/workflow/types' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' import ConfigContext from '@/context/debug-configuration' import { useEventEmitterContextContext } from '@/context/event-emitter' import { useModalContext } from '@/context/modal-context' @@ -58,8 +57,6 @@ const buildPromptVariableFromInput = (payload: InputVar): PromptVariable => { key: variable, name: label as string, } - if (payload.type === InputVarType.textInput) - nextItem.max_length = nextItem.max_length || DEFAULT_VALUE_MAX_LEN if (payload.type !== InputVarType.select) delete nextItem.options diff --git a/web/app/components/app/configuration/debug/chat-user-input.tsx b/web/app/components/app/configuration/debug/chat-user-input.tsx index 11189751e04fab..3f9fdc32be2f31 100644 --- a/web/app/components/app/configuration/debug/chat-user-input.tsx +++ b/web/app/components/app/configuration/debug/chat-user-input.tsx @@ -7,7 +7,6 @@ import Input from '@/app/components/base/input' import Select from '@/app/components/base/select' import Textarea from '@/app/components/base/textarea' import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' import ConfigContext from '@/context/debug-configuration' import { cn } from '@/utils/classnames' @@ -88,7 +87,7 @@ const ChatUserInput = ({ onChange={(e) => { handleInputValueChange(key, e.target.value) }} placeholder={name} autoFocus={index === 0} - maxLength={max_length || DEFAULT_VALUE_MAX_LEN} + maxLength={max_length} /> )} {type === 'paragraph' && ( @@ -115,7 +114,7 @@ const ChatUserInput = ({ onChange={(e) => { handleInputValueChange(key, e.target.value) }} placeholder={name} autoFocus={index === 0} - maxLength={max_length || DEFAULT_VALUE_MAX_LEN} + maxLength={max_length} /> )} {type === 'checkbox' && ( diff --git a/web/app/components/app/configuration/prompt-value-panel/index.tsx b/web/app/components/app/configuration/prompt-value-panel/index.tsx index 9b61b3c7aa7135..613efb87103bb3 100644 --- a/web/app/components/app/configuration/prompt-value-panel/index.tsx +++ b/web/app/components/app/configuration/prompt-value-panel/index.tsx @@ -20,7 +20,6 @@ import Select from '@/app/components/base/select' import Textarea from '@/app/components/base/textarea' import Tooltip from '@/app/components/base/tooltip' import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' import ConfigContext from '@/context/debug-configuration' import { AppModeEnum, ModelModeType } from '@/types/app' import { cn } from '@/utils/classnames' @@ -142,7 +141,7 @@ const PromptValuePanel: FC = ({ onChange={(e) => { handleInputValueChange(key, e.target.value) }} placeholder={name} autoFocus={index === 0} - maxLength={max_length || DEFAULT_VALUE_MAX_LEN} + maxLength={max_length} /> )} {type === 'paragraph' && ( @@ -170,7 +169,7 @@ const PromptValuePanel: FC = ({ onChange={(e) => { handleInputValueChange(key, e.target.value) }} placeholder={name} autoFocus={index === 0} - maxLength={max_length || DEFAULT_VALUE_MAX_LEN} + maxLength={max_length} /> )} {type === 'checkbox' && ( diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hooks.ts b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hooks.ts index 3820d5f1b830ac..80aa879b8f751c 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hooks.ts +++ b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hooks.ts @@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next' import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks' import { InputFieldType } from '@/app/components/base/form/form-scenarios/input-field/types' import { DEFAULT_FILE_UPLOAD_SETTING } from '@/app/components/workflow/constants' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' import { PipelineInputVarType } from '@/models/pipeline' import { useFileUploadConfig } from '@/service/use-common' import { formatFileSize } from '@/utils/format' @@ -87,8 +86,6 @@ export const useConfigurations = (props: { if (type === PipelineInputVarType.multiFiles) setFieldValue('maxLength', DEFAULT_FILE_UPLOAD_SETTING.max_length) } - if (type === PipelineInputVarType.paragraph) - setFieldValue('maxLength', DEFAULT_VALUE_MAX_LEN) }, [setFieldValue]) const handleVariableNameBlur = useCallback((value: string) => { diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.spec.tsx b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.spec.tsx index 0470bd4c68f13a..48df13acb20316 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.spec.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.spec.tsx @@ -779,27 +779,6 @@ describe('useConfigurations', () => { expect(mockSetFieldValue).toHaveBeenCalledWith('maxLength', expect.any(Number)) }) - it('should call setFieldValue when type changes to paragraph', () => { - // Arrange - const mockGetFieldValue = vi.fn() - const mockSetFieldValue = vi.fn() - - const { result } = renderHookWithProviders(() => - useConfigurations({ - getFieldValue: mockGetFieldValue, - setFieldValue: mockSetFieldValue, - supportFile: false, - }), - ) - - // Act - const typeConfig = result.current.find(config => config.variable === 'type') - typeConfig?.listeners?.onChange?.(createMockEvent(PipelineInputVarType.paragraph)) - - // Assert - expect(mockSetFieldValue).toHaveBeenCalledWith('maxLength', 48) // DEFAULT_VALUE_MAX_LEN - }) - it('should set label from variable name on blur when label is empty', () => { // Arrange const mockGetFieldValue = vi.fn().mockReturnValue('') diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index b793a03ce7e9c0..509687e245066b 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -26,7 +26,7 @@ import DifyLogo from '@/app/components/base/logo/dify-logo' import Toast from '@/app/components/base/toast' import Res from '@/app/components/share/text-generation/result' import RunOnce from '@/app/components/share/text-generation/run-once' -import { appDefaultIconBackground, BATCH_CONCURRENCY, DEFAULT_VALUE_MAX_LEN } from '@/config' +import { appDefaultIconBackground, BATCH_CONCURRENCY } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' import { useWebAppStore } from '@/context/web-app-context' import { useAppFavicon } from '@/hooks/use-app-favicon' @@ -256,11 +256,10 @@ const TextGeneration: FC = ({ promptConfig?.prompt_variables.forEach((varItem, varIndex) => { if (errorRowIndex !== 0) return - if (varItem.type === 'string') { - const maxLen = varItem.max_length || DEFAULT_VALUE_MAX_LEN - if (item[varIndex].length > maxLen) { + if (varItem.type === 'string' && varItem.max_length) { + if (item[varIndex].length > varItem.max_length) { moreThanMaxLengthVarName = varItem.name - maxLength = maxLen + maxLength = varItem.max_length errorRowIndex = index + 1 return } diff --git a/web/app/components/share/text-generation/run-once/index.spec.tsx b/web/app/components/share/text-generation/run-once/index.spec.tsx index 8882253d0ea95c..ea5ce3c902af98 100644 --- a/web/app/components/share/text-generation/run-once/index.spec.tsx +++ b/web/app/components/share/text-generation/run-once/index.spec.tsx @@ -236,4 +236,46 @@ describe('RunOnce', () => { const stopButton = screen.getByTestId('stop-button') expect(stopButton).toBeDisabled() }) + + describe('maxLength behavior', () => { + it('should not have maxLength attribute when max_length is not set', async () => { + const promptConfig: PromptConfig = { + prompt_template: 'template', + prompt_variables: [ + createPromptVariable({ + key: 'textInput', + name: 'Text Input', + type: 'string', + // max_length is not set + }), + ], + } + const { onInputsChange } = setup({ promptConfig, visionConfig: { ...baseVisionConfig, enabled: false } }) + await waitFor(() => { + expect(onInputsChange).toHaveBeenCalled() + }) + const input = screen.getByPlaceholderText('Text Input') + expect(input).not.toHaveAttribute('maxLength') + }) + + it('should have maxLength attribute when max_length is set', async () => { + const promptConfig: PromptConfig = { + prompt_template: 'template', + prompt_variables: [ + createPromptVariable({ + key: 'textInput', + name: 'Text Input', + type: 'string', + max_length: 100, + }), + ], + } + const { onInputsChange } = setup({ promptConfig, visionConfig: { ...baseVisionConfig, enabled: false } }) + await waitFor(() => { + expect(onInputsChange).toHaveBeenCalled() + }) + const input = screen.getByPlaceholderText('Text Input') + expect(input).toHaveAttribute('maxLength', '100') + }) + }) }) diff --git a/web/app/components/share/text-generation/run-once/index.tsx b/web/app/components/share/text-generation/run-once/index.tsx index b8193fd9440370..ca29ce1a989bfa 100644 --- a/web/app/components/share/text-generation/run-once/index.tsx +++ b/web/app/components/share/text-generation/run-once/index.tsx @@ -19,7 +19,6 @@ import Textarea from '@/app/components/base/textarea' import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { cn } from '@/utils/classnames' @@ -140,7 +139,7 @@ const RunOnce: FC = ({ placeholder={item.name} value={inputs[item.key]} onChange={(e: ChangeEvent) => { handleInputsChange({ ...inputsRef.current, [item.key]: e.target.value }) }} - maxLength={item.max_length || DEFAULT_VALUE_MAX_LEN} + maxLength={item.max_length} /> )} {item.type === 'paragraph' && ( diff --git a/web/config/index.ts b/web/config/index.ts index b804629048ad24..08ce14b264971c 100644 --- a/web/config/index.ts +++ b/web/config/index.ts @@ -208,7 +208,6 @@ export const VAR_ITEM_TEMPLATE = { key: '', name: '', type: 'string', - max_length: DEFAULT_VALUE_MAX_LEN, required: true, } @@ -216,7 +215,6 @@ export const VAR_ITEM_TEMPLATE_IN_WORKFLOW = { variable: '', label: '', type: InputVarType.textInput, - max_length: DEFAULT_VALUE_MAX_LEN, required: true, options: [], } @@ -225,7 +223,6 @@ export const VAR_ITEM_TEMPLATE_IN_PIPELINE = { variable: '', label: '', type: PipelineInputVarType.textInput, - max_length: DEFAULT_VALUE_MAX_LEN, required: true, options: [], } diff --git a/web/utils/var.ts b/web/utils/var.ts index 4f572d776869c3..1851084b2eecb0 100644 --- a/web/utils/var.ts +++ b/web/utils/var.ts @@ -30,7 +30,7 @@ export const getNewVar = (key: string, type: string) => { } export const getNewVarInWorkflow = (key: string, type = InputVarType.textInput): InputVar => { - const { max_length: _maxLength, ...rest } = VAR_ITEM_TEMPLATE_IN_WORKFLOW + const { ...rest } = VAR_ITEM_TEMPLATE_IN_WORKFLOW if (type !== InputVarType.textInput) { return { ...rest,