Skip to content

Commit

Permalink
Merge pull request #7 from WP-Autoplugin/version-1-2
Browse files Browse the repository at this point in the history
Version 1.2
  • Loading branch information
balazspiller authored Jan 10, 2025
2 parents 6580ae2 + 0767db4 commit a9cc01a
Show file tree
Hide file tree
Showing 37 changed files with 6,254 additions and 268 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ WP-Autoplugin is a free WordPress plugin that uses AI to assist in generating, f
- Generate plugins using AI
- Fix and extend existing plugins
- Full control over the generation process
- Support for multiple AI models (OpenAI, Anthropic, Google Gemini, xAI Grok)
- Support for multiple AI models, and any OpenAI-compatible custom API
- View the list of generated plugins for easy management

---
Expand All @@ -25,7 +25,9 @@ WP-Autoplugin offers practical solutions for various WordPress development scena
- **Completely Free**: No premium version, no ads, no account required.
- **Open Source**: Contributions are welcome.
- **Privacy-Focused**: No data collection or external communication (except for the AI API you choose).
- **BYOK (Bring Your Own Key)**: Use your own OpenAI, Anthropic, Google AI Studio, or xAI API key.
- **BYOK (Bring Your Own Key)**: Use your own API key from the AI provider of your choice.
- **Flexible AI Models**: Choose from a variety of AI models to suit your needs, or set up custom models.
- **Use in Your Language**: The plugin is fully translatable and has built-in support for 10+ languages.

## How It Works

Expand Down Expand Up @@ -61,6 +63,10 @@ WP-Autoplugin supports various models:

We recommend using the **Claude 3.5 Sonnet** model for optimal results. While WP-Autoplugin is free to use, you may need to pay for API usage based on your chosen model.

## Custom Models

WP-Autoplugin also supports custom models: you can plug in any OpenAI-compatible API by providing the endpoint URL, model name, and API key. This feature allows you to use any model you have access to, including locally hosted models, or custom models you've trained yourself.

## BYOK (Bring Your Own Key)

To use WP-Autoplugin, you'll need an API key from OpenAI, Anthropic, Google AI Studio, or xAi. Insert your key in the plugin settings to get started. Your API key remains on your server and is not shared with anyone.
Expand Down Expand Up @@ -123,12 +129,32 @@ WP-Autoplugin has some limitations to be aware of:

Or watch the [WP-Autoplugin demo video on Youtube](https://www.youtube.com/watch?v=b36elwTLfa4) that shows how it generates a plugin and fixes a bug.

## Translations

WP-Autoplugin is fully translatable. If you would like to contribute a translation, please create a pull request with the translation files. Currently, the plugin includes translations for the following languages:
- English - `en_US`
- Français (French) - `fr_FR`
- Español (Spanish) - `es_ES`
- Deutsch (German) - `de_DE`
- Português (Portuguese) - `pt_PT`
- Italiano (Italian) - `it_IT`
- Magyar (Hungarian) - `hu_HU`
- Nederlands (Dutch) - `nl_NL`
- Polski (Polish) - `pl_PL`
- Türkçe (Turkish) - `tr_TR`
- Русский (Russian) - `ru_RU`

## Licensing

WP-Autoplugin is licensed under the GPLv3 or later.

## Changelog

### 1.2
- Added support for any OpenAI-compatible API with the custom models option
- Added translations for 10 more languages
- Fixed PHP notice on "Add New Plugin" screen.

### 1.1.2
- Added support for Google Gemini Flash 2.0 Thinking model
- Added support for xAI Grok-2-1212 model
Expand Down
123 changes: 120 additions & 3 deletions includes/admin/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public function __construct() {
add_action( 'wp_ajax_wp_autoplugin_' . $action, array( $this, 'ajax_actions' ) );
}

// Add or delete custom model AJAX action.
add_action( 'wp_ajax_wp_autoplugin_add_model', array( $this, 'ajax_add_model' ) );
add_action( 'wp_ajax_wp_autoplugin_remove_model', array( $this, 'ajax_remove_model' ) );

// Show notices.
add_action( 'admin_notices', array( $this, 'show_notices' ) );

Expand All @@ -133,6 +137,9 @@ public function get_api( $model ) {
$anthropic_api_key = get_option( 'wp_autoplugin_anthropic_api_key' );
$google_api_key = get_option( 'wp_autoplugin_google_api_key' );
$xai_api_key = get_option( 'wp_autoplugin_xai_api_key' );
$custom_models = get_option( 'wp_autoplugin_custom_models', array() );

$api = null;

if ( ! empty( $openai_api_key ) && array_key_exists( $model, self::$models['OpenAI'] ) ) {
$api = new OpenAI_API();
Expand All @@ -150,10 +157,26 @@ public function get_api( $model ) {
$api = new XAI_API();
$api->set_api_key( $xai_api_key );
$api->set_model( $model );
} else {
$api = null;
}

// Check custom models:
if ( ! empty( $custom_models ) ) {
foreach ( $custom_models as $custom_model ) {
// If the "modelParameter" in the DB matches the user’s selected $model.
if ( $custom_model['name'] === $model ) {
$api = new Custom_API();
$api->set_custom_config(
$custom_model['url'],
$custom_model['apiKey'],
$custom_model['modelParameter'],
$custom_model['headers']
);
return $api;
}
}
}

// If nothing matches, $api will be null.
return $api;
}

Expand Down Expand Up @@ -509,7 +532,7 @@ public function enqueue_scripts() {
*/
public function add_settings_link( $links ) {
$settings_link = '<a href="' . admin_url( 'admin.php?page=wp-autoplugin-settings' ) . '">' . __( 'Settings', 'wp-autoplugin' ) . '</a>';
$generate_link = '<a href="' . admin_url( 'admin.php?page=wp-autoplugin-generate' ) . '">' . __( 'Generate New Plugin', 'wp-autoplugin' ) . '</a>';
$generate_link = '<a href="' . admin_url( 'admin.php?page=wp-autoplugin-generate' ) . '">' . __( 'Generate Plugin', 'wp-autoplugin' ) . '</a>';
array_unshift( $links, $settings_link, $generate_link );
return $links;
}
Expand Down Expand Up @@ -711,6 +734,100 @@ public function ajax_extend_plugin() {
wp_send_json_success( $result );
}

/**
* AJAX handler for adding a custom model.
*
* @return void
*/
public function ajax_add_model() {
// Verify nonce
if ( ! check_ajax_referer( 'wp_autoplugin_nonce', 'nonce', false ) ) {
wp_send_json_error( array(
'message' => __( 'Security check failed', 'wp-autoplugin' ),
) );
}

// Get and validate model data
$model = isset( $_POST['model'] ) ? $_POST['model'] : null;
if ( ! $model || ! isset( $model['name'] ) || ! isset( $model['url'] ) || ! isset( $model['apiKey'] ) ) {
wp_send_json_error( array(
'message' => __( 'Invalid model data', 'wp-autoplugin' ),
) );
}

// Sanitize input
$new_model = array(
'name' => sanitize_text_field( $model['name'] ),
'url' => esc_url_raw( $model['url'] ),
'modelParameter' => sanitize_text_field( $model['modelParameter'] ),
'apiKey' => sanitize_text_field( $model['apiKey'] ),
'headers' => array_map(
'sanitize_text_field',
isset( $model['headers'] ) ? (array) $model['headers'] : array()
),
);

// Get existing models
$models = get_option( 'wp_autoplugin_custom_models', array() );
if ( ! is_array( $models ) ) {
$models = array();
}

// Add new model
$models[] = $new_model;

// Update option
update_option( 'wp_autoplugin_custom_models', $models );

// Send success response
wp_send_json_success( array(
'models' => $models,
'message' => __( 'Model added successfully', 'wp-autoplugin' ),
) );
}

/**
* AJAX handler for removing a custom model.
*
* @return void
*/
public function ajax_remove_model() {
// Verify nonce
if ( ! check_ajax_referer( 'wp_autoplugin_nonce', 'nonce', false ) ) {
wp_send_json_error( array(
'message' => __( 'Security check failed', 'wp-autoplugin' ),
) );
}

// Get existing models
$models = get_option( 'wp_autoplugin_custom_models', array() );
if ( ! is_array( $models ) ) {
$models = array();
}

// Get and validate model index
$index = isset( $_POST['index'] ) ? intval( $_POST['index'] ) : null;
if ( ! is_int( $index ) || $index >= count( $models ) ) {
wp_send_json_error( array(
'message' => __( 'Invalid model index', 'wp-autoplugin' ),
) );
}

// Remove model
if ( isset( $models[ $index ] ) ) {
unset( $models[ $index ] );
}

// Update option
update_option( 'wp_autoplugin_custom_models', $models );

// Send success response
wp_send_json_success( array(
'models' => $models,
'message' => __( 'Model removed successfully', 'wp-autoplugin' ),
) );
}

/**
* Show admin notices.
* This function is hooked to the `admin_notices` action.
Expand Down
137 changes: 137 additions & 0 deletions includes/api/class-custom-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php
/**
* Custom API class.
*
* @package WP-Autoplugin
* @since 1.2
* @version 1.2
* @link https://wp-autoplugin.com
* @license GPL-2.0+
* @license https://www.gnu.org/licenses/gpl-2.0.html
*/

namespace WP_Autoplugin;

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Custom API class that connects to user-defined OpenAI-compatible endpoints.
*/
class Custom_API extends OpenAI_API {

/**
* Additional headers specified by the user.
*
* @var array
*/
protected $extra_headers = array();

/**
* Configure the custom API with the user-defined settings.
*
* @param string $endpoint The custom API endpoint (url).
* @param string $api_key The API key for authentication.
* @param string $model The model parameter sent to the API.
* @param array $headers Additional headers (key/value pairs).
*/
public function set_custom_config( $endpoint, $api_key, $model, $headers = array() ) {
$this->api_url = $endpoint;
$this->api_key = $api_key;
$this->model = $model;
$this->extra_headers = $this->parse_extra_headers( $headers );
}

/**
* Override the send_prompt to include user-defined headers.
*
* @param string $prompt The user prompt.
* @param string $system_message Optional system message.
* @param array $override_body Optional parameters to override in the request body.
*
* @return string|\WP_Error The response or a WP_Error object on failure.
*/
public function send_prompt( $prompt, $system_message = '', $override_body = array() ) {
$prompt = $this->trim_prompt( $prompt );

$messages = array();
if ( $system_message ) {
$messages[] = array(
'role' => 'system',
'content' => $system_message,
);
}

$messages[] = array(
'role' => 'user',
'content' => $prompt,
);

$body = array(
'model' => $this->model,
'temperature' => $this->temperature,
'max_tokens' => $this->max_tokens,
'messages' => $messages,
);

// Only keep valid keys from $override_body.
$allowed_keys = $this->get_allowed_parameters();
$override_body = array_intersect_key( $override_body, array_flip( $allowed_keys ) );
$body = array_merge( $body, $override_body );

// Merge default auth header with any extra headers.
$headers = array_merge(
array(
'Authorization' => 'Bearer ' . $this->api_key,
'Content-Type' => 'application/json',
),
$this->extra_headers
);

$response = wp_remote_post(
$this->api_url,
array(
'timeout' => 60,
'headers' => $headers,
'body' => wp_json_encode( $body ),
)
);

if ( is_wp_error( $response ) ) {
return $response;
}

$data = json_decode( wp_remote_retrieve_body( $response ), true );

if ( empty( $data['choices'][0]['message']['content'] ) ) {
return new \WP_Error(
'api_error',
__( 'Error communicating with the API.', 'wp-autoplugin' ) . "\n" . print_r( $data, true )
);
}

return $data['choices'][0]['message']['content'];
}

/**
* Convert the user’s header lines into an associative array.
*
* @param array $headers Array of lines like ["X-Test=Value", "Accept=application/json"].
* @return array Key-value pairs for use in wp_remote_post header.
*/
protected function parse_extra_headers( $headers ) {
$parsed = array();
foreach ( $headers as $header_line ) {
if ( strpos( $header_line, '=' ) !== false ) {
list( $key, $value ) = explode( '=', $header_line, 2 );
$key = trim( $key );
$value = trim( $value );
if ( $key && $value ) {
$parsed[ $key ] = $value;
}
}
}
return $parsed;
}
}
2 changes: 1 addition & 1 deletion includes/api/class-openai-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class OpenAI_API extends API {
*
* @var float
*/
private $temperature = 0.2;
protected $temperature = 0.2;

/**
* Max tokens parameter.
Expand Down
2 changes: 2 additions & 0 deletions includes/class-github-updater.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,9 @@ public function api_check( $transient ) {
if ( 1 === $update ) {
$response = new \stdClass();
$response->new_version = $this->config['new_version'];
$response->id = $this->config['slug'];
$response->slug = $this->config['proper_folder_name'];
$response->plugin = $this->config['slug'];
$response->url = $this->config['github_url'];
$response->package = $this->config['zip_url'];

Expand Down
Binary file added languages/wp-autoplugin-de_DE.mo
Binary file not shown.
Loading

0 comments on commit a9cc01a

Please sign in to comment.