-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AI Module(Simplified Version) #2555
base: develop
Are you sure you want to change the base?
Conversation
WalkthroughThis pull request integrates AI capabilities into the project by adding new dependency management classes and services alongside a dedicated REST controller and API endpoints. Key additions include an Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant UI as DokanAI Component
participant AP as API Module (generateAiContent)
participant RC as AIRequestController
participant SL as ServiceLocator
participant AS as AI Service (ChatGPT / Gemini)
U->>UI: Enter prompt and submit
UI->>AP: Call generateAiContent(prompt, field)
AP->>RC: Send POST request to /dokan/v1/ai/generate
RC->>SL: Resolve service based on AI engine
SL->>AS: Instantiate appropriate AI service
AS->>AS: Process prompt and send API request
AS-->>RC: Return generated content or error
RC-->>AP: Return REST response with content
AP-->>UI: Deliver AI-generated content
UI->>U: Display generated response in modal
Possibly related PRs
Suggested labels
Poem
Tip 🌐 Web search-backed reviews and chat
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
🧹 Nitpick comments (18)
includes/Intelligence/Services/AbstractAIService.php (3)
7-20
: Consider adding a descriptive class-level docblock and constructor documentation.Currently, there's no high-level docblock explaining the purpose and usage of this abstract class. Adding a short summary can help maintainers and consumers quickly understand its responsibilities. Similarly, documenting what
load_configuration
is expected to do will reduce confusion for subclass implementers.
29-34
: Use consistent error handling patterns for API key checks.While
validate_api_key
returns a boolean, your other methods often returnWP_Error
objects if critical data is missing. Consider returning aWP_Error
here or merging validation steps into a single place to keep error-handling consistent.
42-56
: Add JSON headers and a timeout to wp_remote_post.When sending JSON via
wp_remote_post
, it's best practice to include'Content-Type' => 'application/json'
. Also consider a short timeout to prevent lengthy stalls, for instance:'timeout' => 15
.'headers' => array_merge( + [ 'Content-Type' => 'application/json' ], $params['headers'] ?? [] ), 'body' => wp_json_encode( $params['data'] ), + 'timeout' => 15,src/intelligence/types/index.ts (1)
1-5
: Add JSDoc documentation for the Field interface.Consider adding comprehensive JSDoc documentation to explain:
- The purpose of the Field interface
- Expected values and use cases for each property
- Specific meaning of 'section' type
+/** + * Represents a field configuration for AI content generation + * @interface Field + * @property {string} id - Unique identifier for the field + * @property {string} title - Display title of the field + * @property {'input' | 'section'} type - Type of the field: + * - 'input': For user input fields + * - 'section': For content section containers + */ export interface Field { id: string; title: string; type: 'input' | 'section'; // Update types based on API response }includes/Intelligence/Services/AIResponseServiceInterface.php (3)
13-13
: Add return type hint for process method.The method should specify its return type for better type safety.
- public function process( string $prompt, $payload = null ); + public function process( string $prompt, $payload = null ): mixed;
15-15
: Add parameter and return type hints for send_request method.The method should specify parameter types and return type for better type safety.
- public function send_request( array $params ); + public function send_request( array $params ): mixed;
5-22
: Enhance interface documentation.Consider adding comprehensive PHPDoc blocks for each method to explain:
- Expected parameter values
- Return value format
- Possible exceptions
interface AIResponseServiceInterface { + /** + * Process the AI request and return a response. + * + * @param string $prompt The input prompt for the AI. + * @param mixed|null $payload Optional additional data. + * @return mixed The response from the AI. + * @throws \Exception When AI processing fails. + */ public function process( string $prompt, $payload = null ): mixed; + /** + * Send a request to the AI service. + * + * @param array $params Request parameters. + * @return mixed Response from the AI service. + * @throws \Exception When request fails. + */ public function send_request( array $params ): mixed; + /** + * Set the API key for the AI service. + * + * @throws \Exception When API key is invalid or missing. + */ public function set_api_key(): void; + /** + * Get available AI models. + * + * @return array List of available models. + */ public function get_models(): array; + /** + * Validate the API key. + * + * @return bool True if API key is valid, false otherwise. + */ public function validate_api_key(): bool; }includes/Intelligence/Utils/PromptUtils.php (2)
12-20
: Add input validation and caching for supported fields.The method could benefit from input validation and caching of supported fields to improve performance.
+ private static $supported_fields_cache = null; + public static function get_personalized_prompt( string $id ): ?string { - foreach ( AISupportedFields::get_supported_fields() as $field ) { + if ( empty( $id ) ) { + return null; + } + + if ( self::$supported_fields_cache === null ) { + self::$supported_fields_cache = AISupportedFields::get_supported_fields(); + } + + foreach ( self::$supported_fields_cache as $field ) { if ( $field['id'] === $id ) { return $field['personalized_prompt']; } } - return null; // Return null if the key is not found }
28-36
: Improve string concatenation and validation in prepare_prompt.Consider using sprintf for string formatting and add input validation.
public static function prepare_prompt( string $id, string $prompt ): string { + if ( empty( $id ) || empty( $prompt ) ) { + throw new \InvalidArgumentException( 'ID and prompt cannot be empty' ); + } + $personalized_prompt = self::get_personalized_prompt( $id ); if ( $personalized_prompt ) { - return $personalized_prompt . $prompt; + return sprintf( '%s %s', $personalized_prompt, $prompt ); } return $prompt; }includes/DependencyManagement/Providers/IntelligenceServiceProvider.php (1)
34-44
: Consider adding error handling for service instantiation.The service instantiation could fail if the class constructor throws an exception.
public function register(): void { foreach ( $this->services as $service ) { $definition = $this->getContainer() ->addShared( $service, function () use ( $service ) { - return new $service(); + try { + return new $service(); + } catch ( \Exception $e ) { + error_log( sprintf( 'Failed to instantiate service %s: %s', $service, $e->getMessage() ) ); + return null; + } } ); $this->add_tags( $definition, self::TAGS ); } }includes/Intelligence/Utils/AISupportedFields.php (1)
7-48
: LGTM! Well-structured field definitions with proper internationalization.The implementation follows WordPress coding standards with proper translation functions and provides extensibility through filters.
However, consider adding PHPDoc for the method to document the return type structure.
+ /** + * Get the list of AI-supported fields. + * + * @return array{ + * id: string, + * personalized_prompt: string, + * title: string, + * type: 'input'|'section' + * }[] List of supported fields. + */ public static function get_supported_fields(): array {includes/Intelligence/Services/ChatgptResponseService.php (1)
33-67
: Add request timeout and rate limiting.The process method should include timeout configuration and rate limiting to prevent API abuse.
$params = [ 'url' => self::API_URL, 'headers' => [ 'Authorization' => 'Bearer ' . $this->api_key, 'Content-Type' => 'application/json', ], 'data' => $request_data, + 'timeout' => 30, ];
includes/Intelligence/Services/GeminiResponseService.php (1)
43-56
: Consider making safety settings configurable.The safety settings and generation config are hardcoded. Consider making these configurable through WordPress options.
+'safetySettings' => apply_filters('dokan_ai_gemini_safety_settings', [ [ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'threshold' => 'BLOCK_ONLY_HIGH', ], ]),
src/intelligence/utils/dom.ts (2)
1-5
: Improve type safety for tinymce declaration.The
any
type for tinymce should be replaced with a proper type definition.- tinymce: any; + tinymce: { + get: (id: string) => TinyMCEEditor | null; + };
47-63
: Enhance element selection strategy.The current selector strategy might be too broad. Consider adding data attributes specifically for AI field targeting.
const selectors = [ + `[data-dokan-ai-field="${fieldId}"]`, // Preferred selector `input[name="${fieldId}"]`, `#${fieldId}`, `textarea[name="${fieldId}"]`, `[data-id="${fieldId}"]`, ];
src/intelligence/components/DokanAI.tsx (1)
7-240
: Wrap component with error boundary.Consider wrapping the component with an error boundary to gracefully handle runtime errors.
Create an error boundary component:
class AIErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError(error) { return { hasError: true }; } render() { if (this.state.hasError) { return <div>Something went wrong with AI generation.</div>; } return this.props.children; } } // Usage: export default function WrappedDokanAI(props) { return ( <AIErrorBoundary> <DokanAI {...props} /> </AIErrorBoundary> ); }src/intelligence/styles/modal.css (2)
22-24
: Consider using CSS custom properties for z-index management.Using a high z-index value directly can lead to z-index wars. Consider using CSS custom properties for better maintainability.
+:root { + --dokan-modal-z-index: 1000; +} body:has(.dokan-ai-modal) .z-10[aria-modal="true"][data-headlessui-state="open"] { - z-index: 1000; + z-index: var(--dokan-modal-z-index); }
19-21
: Consider more specific selector for padding override.The current selector might affect unrelated elements. Consider using a more specific selector.
-body:has(.dokan-ai-prompt-icon) .mce-path { +.dokan-ai-container .mce-path { padding: 10px; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
assets/images/dokan-ai.svg
is excluded by!**/*.svg
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (19)
includes/DependencyManagement/Providers/IntelligenceServiceProvider.php
(1 hunks)includes/DependencyManagement/Providers/ServiceProvider.php
(1 hunks)includes/Intelligence/Manager.php
(1 hunks)includes/Intelligence/REST/AIRequestController.php
(1 hunks)includes/Intelligence/Services/AIResponseServiceInterface.php
(1 hunks)includes/Intelligence/Services/AbstractAIService.php
(1 hunks)includes/Intelligence/Services/ChatgptResponseService.php
(1 hunks)includes/Intelligence/Services/GeminiResponseService.php
(1 hunks)includes/Intelligence/Services/ServiceLocator.php
(1 hunks)includes/Intelligence/Utils/AISupportedFields.php
(1 hunks)includes/Intelligence/Utils/PromptUtils.php
(1 hunks)package.json
(2 hunks)src/intelligence/components/DokanAI.tsx
(1 hunks)src/intelligence/main.tsx
(1 hunks)src/intelligence/styles/modal.css
(1 hunks)src/intelligence/types/index.ts
(1 hunks)src/intelligence/utils/api.ts
(1 hunks)src/intelligence/utils/dom.ts
(1 hunks)webpack.config.js
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: api tests (1, 1)
- GitHub Check: e2e tests (1, 3)
🔇 Additional comments (10)
package.json (2)
36-37
: Updated TailwindCSS and Added TypeScript Dependency
- The TailwindCSS dependency is updated from
^3.3.3
to^3.4.17
. Please ensure that any potential breaking changes or required configuration updates with the new version are addressed.- The addition of TypeScript (
"typescript": "^5.7.3"
) as a dev dependency is a solid move to enhance type safety, especially with the new AI module components.
54-55
: New Production Dependencies Introduced
- The new dependencies
"@getdokan/dokan-ui": "^1.0.19"
and"@wordpress/element": "^6.17.0"
are now included. Verify that these libraries integrate well with the existing codebase, and that their APIs align with the new UI components and AI module requirements.includes/Intelligence/Services/AbstractAIService.php (1)
58-78
: Guard against malformed or null response data.
json_decode
can returnnull
if the body isn't valid JSON. It's good practice to check fornull
and handle the case where decoding fails, returning a clearWP_Error
.includes/Intelligence/REST/AIRequestController.php (1)
80-109
: Verify AI service type and guard against oversized inputs.Before calling
$service->process(...)
, confirm that$service
implements the required interface. Additionally, parsing a very large prompt or payload can degrade performance, so consider limiting payload size or adding additional checks.includes/Intelligence/Manager.php (2)
30-38
: Clean separation of settings vs. configured actions is well-structured.The conditional branching to either load settings or fully enable AI actions is straightforward and maintainable. Nice job keeping the logic simple.
45-61
: Confirm presence and validity of API keys.When checking
dokan_ai_{engine}_api_key
, consider validating token format or length. If any new engines are added, update this check logic to maintain consistency.includes/DependencyManagement/Providers/IntelligenceServiceProvider.php (1)
9-18
: LGTM! Well-structured service provider implementation.The class follows SOLID principles with clear separation of concerns and good documentation.
includes/DependencyManagement/Providers/ServiceProvider.php (1)
65-65
: LGTM! Consider load order implications.The addition of IntelligenceServiceProvider looks good. Ensure it doesn't have dependencies on services from other providers that might not be registered yet.
✅ Verification successful
Service Provider Registration Looks Correct—Load Order Appears Unaffected
The
IntelligenceServiceProvider
registers itsManager
service without relying on other providers’ services, so there aren’t any load order concerns evident in the current implementation. Just ensure that if additional dependencies are added in the future, they’re loaded appropriately.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for service dependencies ast-grep --pattern 'class IntelligenceServiceProvider { $$$ public function register() { $$$ } $$$ }'Length of output: 115
Script:
#!/bin/bash # Search for the class definition of IntelligenceServiceProvider using rg echo ">>> Searching for 'class IntelligenceServiceProvider' occurrences:" rg -n "class\s\+IntelligenceServiceProvider" . echo "" # Show the context around the IntelligenceServiceProvider class definition to inspect its register method echo ">>> Showing context for IntelligenceServiceProvider (first 50 lines after definition):" rg -n "class\s\+IntelligenceServiceProvider" -A 50 echo "" # Additionally, search for any call to the register method within files mentioning IntelligenceServiceProvider echo ">>> Searching for 'function register(' within files that mention IntelligenceServiceProvider:" rg -n "class\s\+IntelligenceServiceProvider" -A 100 | rg -n "function\s\+register\s*\("Length of output: 748
Script:
#!/bin/bash # First, search for any files named IntelligenceServiceProvider.php in the repository echo ">>> Searching for IntelligenceServiceProvider.php files:" fd -HI "IntelligenceServiceProvider\.php" echo "" # If any files are found, display their contents (limited to the first 200 lines for brevity) echo ">>> Displaying contents of IntelligenceServiceProvider.php (first 200 lines):" fd -HI "IntelligenceServiceProvider\.php" -x head -n 200 {}Length of output: 1976
webpack.config.js (2)
61-62
: LGTM! Entry point correctly configured.The new entry point for the AI module is properly integrated into the webpack configuration.
75-75
: LGTM! TypeScript support properly configured.The resolve.extensions array correctly includes TypeScript file extensions (.ts, .tsx).
public function get_items_permissions_check( $request ): bool { | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restricting the permission callback is strongly recommended.
Granting unconditional access (always returning true
) may expose the endpoint to unauthorized users. Implement capability checks or role restrictions to secure this route.
public function get_request_args(): array { | ||
return [ | ||
'prompt' => [ | ||
'type' => 'string', | ||
'required' => true, | ||
'description' => __( 'Prompt to process', 'dokan-lite' ), | ||
], | ||
'id' => [ // Added id parameter | ||
'type' => 'string', | ||
'required' => true, | ||
'description' => __( 'ID of the field for personalized prompt', 'dokan-lite' ), | ||
], | ||
'payload' => [ | ||
'type' => 'array', | ||
'required' => false, | ||
'description' => __( 'Optional data payload', 'dokan-lite' ), | ||
], | ||
]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance request argument validation and sanitization.
Although 'prompt'
and 'id'
are declared as strings, consider sanitizing them. Also, add length or character checks if large or special inputs can harm performance or cause security issues (e.g., injection attempts).
public function render_ai_settings( array $settings_fields ): array { | ||
$gpt_response_service = new ChatgptResponseService(); | ||
$gemini_response_service = new GeminiResponseService(); | ||
|
||
$chatgpt_models = $gpt_response_service->get_models(); | ||
$gemini_models = $gemini_response_service->get_models(); | ||
|
||
$settings_fields['dokan_ai'] = [ | ||
'dokan_ai_engine' => [ | ||
'name' => 'dokan_ai_engine', | ||
'label' => __( 'Engine', 'dokan-lite' ), | ||
'type' => 'select', | ||
'options' => $this->engines, | ||
'desc' => __( 'Select AI engine', 'dokan-lite' ), | ||
'default' => 'chatgpt', | ||
], | ||
'dokan_ai_chatgpt_model' => [ | ||
'name' => 'dokan_ai_chatgpt_model', | ||
'label' => __( 'ChatGPT Model', 'dokan-lite' ), | ||
'type' => 'select', | ||
'options' => $chatgpt_models, | ||
'desc' => __( 'Select your model', 'dokan-lite' ), | ||
'default' => 'gpt-3.5-turbo', | ||
'show_if' => [ | ||
'dokan_ai_engine' => [ | ||
'equal' => 'chatgpt', | ||
], | ||
], | ||
], | ||
'dokan_ai_gemini_model' => [ | ||
'name' => 'dokan_ai_gemini_model', | ||
'label' => __( 'Gemini Model', 'dokan-lite' ), | ||
'type' => 'select', | ||
'options' => $gemini_models, | ||
'desc' => __( 'Select your model', 'dokan-lite' ), | ||
'default' => 'gemini-1.5-flash', | ||
'show_if' => [ | ||
'dokan_ai_engine' => [ | ||
'equal' => 'gemini', | ||
], | ||
], | ||
], | ||
'dokan_ai_gemini_api_key' => [ | ||
'name' => 'dokan_ai_gemini_api_key', | ||
'label' => __( 'Gemini API Key', 'dokan-lite' ), | ||
'type' => 'text', | ||
'desc' => __( 'Enter your API key', 'dokan-lite' ), | ||
'default' => '', | ||
'secret_text' => true, | ||
'show_if' => [ | ||
'dokan_ai_engine' => [ | ||
'equal' => 'gemini', | ||
], | ||
], | ||
], | ||
'dokan_ai_chatgpt_api_key' => [ | ||
'name' => 'dokan_ai_chatgpt_api_key', | ||
'label' => __( 'ChatGPT API Key', 'dokan-lite' ), | ||
'type' => 'text', | ||
'desc' => __( 'Enter your API key', 'dokan-lite' ), | ||
'default' => '', | ||
'secret_text' => true, | ||
'show_if' => [ | ||
'dokan_ai_engine' => [ | ||
'equal' => 'chatgpt', | ||
], | ||
], | ||
], | ||
'dokan_ai_max_tokens_for_marketplace' => [ | ||
'name' => 'dokan_ai_max_tokens_for_marketplace', | ||
'label' => __( 'Max Output Tokens for Marketplace', 'dokan-lite' ), | ||
'type' => 'number', | ||
'desc' => __( 'Set the maximum number of tokens for marketplace.', 'dokan-lite' ), | ||
'default' => '200', | ||
], | ||
]; | ||
|
||
return $settings_fields; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Align default tokens in the UI with the localized script.
In the fields 'dokan_ai_max_tokens_for_marketplace'
, the default is 200
, yet 'max_tokens' => ... '250'
is used in the localized script. This can confuse users. Synchronize the defaults for consistency.
- 'dokan_ai_max_tokens_for_marketplace' => [
- 'default' => '200',
- ],
+ 'dokan_ai_max_tokens_for_marketplace' => [
+ 'default' => '250',
+ ],
Committable suggestion skipped: line range outside the PR's diff.
public function enqueue_ai_assets() { | ||
if ( ! dokan_is_seller_dashboard() ) { | ||
return; | ||
} | ||
|
||
wp_register_style( | ||
'dokan-ai-style', | ||
DOKAN_PLUGIN_ASSEST . '/css/dokan-intelligence.css', | ||
[], | ||
filemtime( DOKAN_DIR . '/assets/css/dokan-intelligence.css' ) | ||
); | ||
|
||
wp_register_script( | ||
'dokan-ai-script', | ||
DOKAN_PLUGIN_ASSEST . '/js/dokan-intelligence.js', | ||
[ 'wp-element', 'wp-i18n', 'wp-api-fetch' ], | ||
filemtime( DOKAN_DIR . '/assets/js/dokan-intelligence.js' ), | ||
true | ||
); | ||
|
||
wp_enqueue_style( 'dokan-ai-style' ); | ||
wp_enqueue_script( 'dokan-ai-script' ); | ||
|
||
$supported_fields = AISupportedFields::get_supported_fields(); | ||
|
||
$settings = [ | ||
'engine' => dokan_get_option( 'dokan_ai_engine', 'dokan_ai', 'chatgpt' ), | ||
'gemini_model' => dokan_get_option( 'dokan_ai_gemini_model', 'dokan_ai', 'gemini-1.5-flash' ), | ||
'chatgpt_model' => dokan_get_option( 'dokan_ai_chatgpt_model', 'dokan_ai', 'gpt-3.5-turbo' ), | ||
'temperature' => dokan_get_option( 'dokan_ai_temperature', 'dokan_ai', '0.7' ), | ||
'max_tokens' => dokan_get_option( 'dokan_ai_max_tokens_for_marketplace', 'dokan_ai', '250' ), | ||
'is_configured' => true, | ||
'fields' => $supported_fields, | ||
]; | ||
|
||
wp_localize_script( 'dokan-ai-script', 'dokanAiSettings', $settings ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure 'is_configured' flag reflects actual status.
Even if $this->is_ai_configured
is false, 'is_configured' => true
is set in dokanAiSettings
. Consider referencing $this->is_ai_configured
directly to avoid confusion or UI misrepresentation.
- 'is_configured' => true,
+ 'is_configured' => $this->is_ai_configured,
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public function enqueue_ai_assets() { | |
if ( ! dokan_is_seller_dashboard() ) { | |
return; | |
} | |
wp_register_style( | |
'dokan-ai-style', | |
DOKAN_PLUGIN_ASSEST . '/css/dokan-intelligence.css', | |
[], | |
filemtime( DOKAN_DIR . '/assets/css/dokan-intelligence.css' ) | |
); | |
wp_register_script( | |
'dokan-ai-script', | |
DOKAN_PLUGIN_ASSEST . '/js/dokan-intelligence.js', | |
[ 'wp-element', 'wp-i18n', 'wp-api-fetch' ], | |
filemtime( DOKAN_DIR . '/assets/js/dokan-intelligence.js' ), | |
true | |
); | |
wp_enqueue_style( 'dokan-ai-style' ); | |
wp_enqueue_script( 'dokan-ai-script' ); | |
$supported_fields = AISupportedFields::get_supported_fields(); | |
$settings = [ | |
'engine' => dokan_get_option( 'dokan_ai_engine', 'dokan_ai', 'chatgpt' ), | |
'gemini_model' => dokan_get_option( 'dokan_ai_gemini_model', 'dokan_ai', 'gemini-1.5-flash' ), | |
'chatgpt_model' => dokan_get_option( 'dokan_ai_chatgpt_model', 'dokan_ai', 'gpt-3.5-turbo' ), | |
'temperature' => dokan_get_option( 'dokan_ai_temperature', 'dokan_ai', '0.7' ), | |
'max_tokens' => dokan_get_option( 'dokan_ai_max_tokens_for_marketplace', 'dokan_ai', '250' ), | |
'is_configured' => true, | |
'fields' => $supported_fields, | |
]; | |
wp_localize_script( 'dokan-ai-script', 'dokanAiSettings', $settings ); | |
} | |
public function enqueue_ai_assets() { | |
if ( ! dokan_is_seller_dashboard() ) { | |
return; | |
} | |
wp_register_style( | |
'dokan-ai-style', | |
DOKAN_PLUGIN_ASSEST . '/css/dokan-intelligence.css', | |
[], | |
filemtime( DOKAN_DIR . '/assets/css/dokan-intelligence.css' ) | |
); | |
wp_register_script( | |
'dokan-ai-script', | |
DOKAN_PLUGIN_ASSEST . '/js/dokan-intelligence.js', | |
[ 'wp-element', 'wp-i18n', 'wp-api-fetch' ], | |
filemtime( DOKAN_DIR . '/assets/js/dokan-intelligence.js' ), | |
true | |
); | |
wp_enqueue_style( 'dokan-ai-style' ); | |
wp_enqueue_script( 'dokan-ai-script' ); | |
$supported_fields = AISupportedFields::get_supported_fields(); | |
$settings = [ | |
'engine' => dokan_get_option( 'dokan_ai_engine', 'dokan_ai', 'chatgpt' ), | |
'gemini_model' => dokan_get_option( 'dokan_ai_gemini_model', 'dokan_ai', 'gemini-1.5-flash' ), | |
'chatgpt_model' => dokan_get_option( 'dokan_ai_chatgpt_model', 'dokan_ai', 'gpt-3.5-turbo' ), | |
'temperature' => dokan_get_option( 'dokan_ai_temperature', 'dokan_ai', '0.7' ), | |
'max_tokens' => dokan_get_option( 'dokan_ai_max_tokens_for_marketplace', 'dokan_ai', '250' ), | |
'is_configured' => $this->is_ai_configured, | |
'fields' => $supported_fields, | |
]; | |
wp_localize_script( 'dokan-ai-script', 'dokanAiSettings', $settings ); | |
} |
// @ts-ignore | ||
const content = data.response || data; | ||
return content || __( 'No content generated', 'dokan' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove @ts-ignore and improve type safety.
Instead of using @ts-ignore, define proper types for the API response.
- // @ts-ignore
- const content = data.response || data;
- return content || __( 'No content generated', 'dokan' );
+ interface AIResponse {
+ response?: string;
+ [key: string]: unknown;
+ }
+ const responseData = data as AIResponse;
+ return responseData.response || __( 'No content generated', 'dokan' );
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// @ts-ignore | |
const content = data.response || data; | |
return content || __( 'No content generated', 'dokan' ); | |
interface AIResponse { | |
response?: string; | |
[key: string]: unknown; | |
} | |
const responseData = data as AIResponse; | |
return responseData.response || __( 'No content generated', 'dokan' ); |
return [ | ||
'response' => $response['choices'][0]['message']['content'] ?? '', | ||
'prompt' => $prompt, | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Enhance Response Structure Validation in ChatgptResponseService
- The method directly accesses
$response['choices'][0]['message']['content']
without verifying that the intermediate keys (choices
, the index0
, andmessage
) exist. - Relying solely on the null coalescing operator (
?? ''
) masks cases where the response structure is malformed instead of explicitly handling the error. - It is recommended to validate the response data (for example, using explicit
isset
checks) or return/log a descriptive error when the structure is unexpected.
🔗 Analysis chain
Enhance error response handling.
The response structure should be validated before accessing array indices to prevent undefined index errors.
I'll generate a new script to inspect the snippet of code in question so that we can confirm if additional error handling is required.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for error handling patterns in the codebase
rg -A 5 "choices\[0\]\['message'\]\['content'\]" --type php
Length of output: 73
Script:
#!/bin/bash
# Display lines 60 to 70 of the file to verify the context around error handling.
sed -n '60,70p' includes/Intelligence/Services/ChatgptResponseService.php
Length of output: 262
$params = [ | ||
'url' => self::API_URL . '?key=' . $this->api_key, | ||
'headers' => [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security concern: API key exposure in URL.
The API key should be passed in the Authorization header instead of as a URL parameter to prevent key exposure in logs.
- 'url' => self::API_URL . '?key=' . $this->api_key,
+ 'url' => self::API_URL,
'headers' => [
'Content-Type' => 'application/json',
+ 'Authorization' => 'Bearer ' . $this->api_key,
],
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
$params = [ | |
'url' => self::API_URL . '?key=' . $this->api_key, | |
'headers' => [ | |
$params = [ | |
'url' => self::API_URL, | |
'headers' => [ | |
'Content-Type' => 'application/json', | |
'Authorization' => 'Bearer ' . $this->api_key, | |
], | |
]; |
const handleLabelClick = ( event: any ) => { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
setIsOpen( true ); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add proper TypeScript event type.
Replace any
with the correct event type for better type safety.
-const handleLabelClick = ( event: any ) => {
+const handleLabelClick = ( event: React.MouseEvent<SVGSVGElement> ) => {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const handleLabelClick = ( event: any ) => { | |
event.preventDefault(); | |
event.stopPropagation(); | |
setIsOpen( true ); | |
}; | |
const handleLabelClick = ( event: React.MouseEvent<SVGSVGElement> ) => { | |
event.preventDefault(); | |
event.stopPropagation(); | |
setIsOpen( true ); | |
}; |
const generateContent = async () => { | ||
setError( '' ); | ||
if ( ! prompt.trim() ) { | ||
setError( __( 'Please enter a prompt.', 'dokan' ) ); | ||
return; | ||
} | ||
setIsLoading( true ); | ||
try { | ||
const content = await generateAiContent( prompt, field ); | ||
setResponse( content ); | ||
setResponseHistory( ( prevState ) => { | ||
return [ ...prevState, content ]; | ||
} ); | ||
setCurrentIndex( responseHistory.length ); | ||
} catch ( err ) { | ||
setError( err.message ); | ||
} finally { | ||
setIsLoading( false ); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Prevent memory leaks in async operations.
Add cleanup logic to handle component unmounting during async operations.
const generateContent = async () => {
+ let isSubscribed = true;
setError( '' );
if ( ! prompt.trim() ) {
setError( __( 'Please enter a prompt.', 'dokan' ) );
return;
}
setIsLoading( true );
try {
const content = await generateAiContent( prompt, field );
+ if (isSubscribed) {
setResponse( content );
setResponseHistory( ( prevState ) => {
return [ ...prevState, content ];
} );
setCurrentIndex( responseHistory.length );
+ }
} catch ( err ) {
+ if (isSubscribed) {
setError( err.message );
+ }
} finally {
+ if (isSubscribed) {
setIsLoading( false );
+ }
}
+ return () => {
+ isSubscribed = false;
+ };
};
Committable suggestion skipped: line range outside the PR's diff.
<svg | ||
onClick={ handleLabelClick } | ||
width="24" | ||
height="24" | ||
viewBox="0 0 20 20" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M8.67091 4.01777C8.84169 3.32602 9.82508 3.32602 9.99586 4.01777V4.01777C10.7248 6.97053 13.0303 9.27598 15.9831 10.0049V10.0049C16.6749 10.1757 16.6749 11.1591 15.9831 11.3299V11.3299C13.0303 12.0588 10.7248 14.3642 9.99586 17.317V17.317C9.82508 18.0087 8.84169 18.0087 8.67091 17.317V17.317C7.94194 14.3642 5.63643 12.0588 2.68366 11.3299V11.3299C1.99189 11.1591 1.99189 10.1757 2.68366 10.0049V10.0049C5.63643 9.27598 7.94194 6.97053 8.67091 4.01777V4.01777Z" | ||
fill="url(#paint0_linear_231_101)" | ||
/> | ||
<path | ||
d="M15.9535 1.08615C16.0226 0.806151 16.4207 0.806151 16.4898 1.08615V1.08615C16.7848 2.28131 17.718 3.21447 18.9132 3.50951V3.50951C19.1932 3.57863 19.1932 3.97668 18.9132 4.0458V4.0458C17.718 4.34084 16.7848 5.274 16.4898 6.46916V6.46916C16.4207 6.74916 16.0226 6.74916 15.9535 6.46916V6.46916C15.6584 5.274 14.7253 4.34084 13.5301 4.0458V4.0458C13.2501 3.97668 13.2501 3.57863 13.5301 3.50951V3.50951C14.7253 3.21447 15.6584 2.28131 15.9535 1.08615V1.08615Z" | ||
fill="url(#paint1_linear_231_101)" | ||
/> | ||
<defs> | ||
<linearGradient | ||
id="paint0_linear_231_101" | ||
x1="9.33338" | ||
y1="1.33435" | ||
x2="9.33338" | ||
y2="20.0004" | ||
gradientUnits="userSpaceOnUse" | ||
> | ||
<stop stopColor="#A875FF" /> | ||
<stop offset="1" stopColor="#7D60D6" /> | ||
</linearGradient> | ||
<linearGradient | ||
id="paint1_linear_231_101" | ||
x1="16.2216" | ||
y1="0" | ||
x2="16.2216" | ||
y2="7.55531" | ||
gradientUnits="userSpaceOnUse" | ||
> | ||
<stop stopColor="#A875FF" /> | ||
<stop offset="1" stopColor="#7D60D6" /> | ||
</linearGradient> | ||
</defs> | ||
</svg> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add ARIA attributes for accessibility.
The SVG icon needs proper accessibility attributes.
<svg
onClick={ handleLabelClick }
+ role="button"
+ aria-label={ __( 'Open AI content generator', 'dokan' ) }
width="24"
height="24"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<svg | |
onClick={ handleLabelClick } | |
width="24" | |
height="24" | |
viewBox="0 0 20 20" | |
fill="none" | |
xmlns="http://www.w3.org/2000/svg" | |
> | |
<path | |
d="M8.67091 4.01777C8.84169 3.32602 9.82508 3.32602 9.99586 4.01777V4.01777C10.7248 6.97053 13.0303 9.27598 15.9831 10.0049V10.0049C16.6749 10.1757 16.6749 11.1591 15.9831 11.3299V11.3299C13.0303 12.0588 10.7248 14.3642 9.99586 17.317V17.317C9.82508 18.0087 8.84169 18.0087 8.67091 17.317V17.317C7.94194 14.3642 5.63643 12.0588 2.68366 11.3299V11.3299C1.99189 11.1591 1.99189 10.1757 2.68366 10.0049V10.0049C5.63643 9.27598 7.94194 6.97053 8.67091 4.01777V4.01777Z" | |
fill="url(#paint0_linear_231_101)" | |
/> | |
<path | |
d="M15.9535 1.08615C16.0226 0.806151 16.4207 0.806151 16.4898 1.08615V1.08615C16.7848 2.28131 17.718 3.21447 18.9132 3.50951V3.50951C19.1932 3.57863 19.1932 3.97668 18.9132 4.0458V4.0458C17.718 4.34084 16.7848 5.274 16.4898 6.46916V6.46916C16.4207 6.74916 16.0226 6.74916 15.9535 6.46916V6.46916C15.6584 5.274 14.7253 4.34084 13.5301 4.0458V4.0458C13.2501 3.97668 13.2501 3.57863 13.5301 3.50951V3.50951C14.7253 3.21447 15.6584 2.28131 15.9535 1.08615V1.08615Z" | |
fill="url(#paint1_linear_231_101)" | |
/> | |
<defs> | |
<linearGradient | |
id="paint0_linear_231_101" | |
x1="9.33338" | |
y1="1.33435" | |
x2="9.33338" | |
y2="20.0004" | |
gradientUnits="userSpaceOnUse" | |
> | |
<stop stopColor="#A875FF" /> | |
<stop offset="1" stopColor="#7D60D6" /> | |
</linearGradient> | |
<linearGradient | |
id="paint1_linear_231_101" | |
x1="16.2216" | |
y1="0" | |
x2="16.2216" | |
y2="7.55531" | |
gradientUnits="userSpaceOnUse" | |
> | |
<stop stopColor="#A875FF" /> | |
<stop offset="1" stopColor="#7D60D6" /> | |
</linearGradient> | |
</defs> | |
</svg> | |
<svg | |
onClick={ handleLabelClick } | |
role="button" | |
aria-label={ __( 'Open AI content generator', 'dokan' ) } | |
width="24" | |
height="24" | |
viewBox="0 0 20 20" | |
fill="none" | |
xmlns="http://www.w3.org/2000/svg" | |
> | |
<path | |
d="M8.67091 4.01777C8.84169 3.32602 9.82508 3.32602 9.99586 4.01777V4.01777C10.7248 6.97053 13.0303 9.27598 15.9831 10.0049V10.0049C16.6749 10.1757 16.6749 11.1591 15.9831 11.3299V11.3299C13.0303 12.0588 10.7248 14.3642 9.99586 17.317V17.317C9.82508 18.0087 8.84169 18.0087 8.67091 17.317V17.317C7.94194 14.3642 5.63643 12.0588 2.68366 11.3299V11.3299C1.99189 11.1591 1.99189 10.1757 2.68366 10.0049V10.0049C5.63643 9.27598 7.94194 6.97053 8.67091 4.01777V4.01777Z" | |
fill="url(#paint0_linear_231_101)" | |
/> | |
<path | |
d="M15.9535 1.08615C16.0226 0.806151 16.4207 0.806151 16.4898 1.08615V1.08615C16.7848 2.28131 17.718 3.21447 18.9132 3.50951V3.50951C19.1932 3.57863 19.1932 3.97668 18.9132 4.0458V4.0458C17.718 4.34084 16.7848 5.274 16.4898 6.46916V6.46916C16.4207 6.74916 16.0226 6.74916 15.9535 6.46916V6.46916C15.6584 5.274 14.7253 4.34084 13.5301 4.0458V4.0458C13.2501 3.97668 13.2501 3.57863 13.5301 3.50951V3.50951C14.7253 3.21447 15.6584 2.28131 15.9535 1.08615V1.08615Z" | |
fill="url(#paint1_linear_231_101)" | |
/> | |
<defs> | |
<linearGradient | |
id="paint0_linear_231_101" | |
x1="9.33338" | |
y1="1.33435" | |
x2="9.33338" | |
y2="20.0004" | |
gradientUnits="userSpaceOnUse" | |
> | |
<stop stopColor="#A875FF" /> | |
<stop offset="1" stopColor="#7D60D6" /> | |
</linearGradient> | |
<linearGradient | |
id="paint1_linear_231_101" | |
x1="16.2216" | |
y1="0" | |
x2="16.2216" | |
y2="7.55531" | |
gradientUnits="userSpaceOnUse" | |
> | |
<stop stopColor="#A875FF" /> | |
<stop offset="1" stopColor="#7D60D6" /> | |
</linearGradient> | |
</defs> | |
</svg> |
All Submissions:
Changes proposed in this Pull Request:
Related Pull Request(s)
Closes
How to test the changes in this Pull Request:
Changelog entry
Before Changes
Describe the issue before changes with screenshots(s).
After Changes
Describe the issue after changes with screenshot(s).
Feature Video (optional)
Link of detailed video if this PR is for a feature.
PR Self Review Checklist:
FOR PR REVIEWER ONLY:
Summary by CodeRabbit
New Features
Chores/Dependencies
Style