fix(ci): phpcbf auto-format + add missing bootstrap requires for tests
212 PHPCS formatting errors auto-fixed by phpcbf across 18 files. All remaining issues are warnings (exit 2), not errors — CI treats exit 2 as success via the same pattern as peptide-news-plugin. Also adds class-psa-ai-content.php and class-psa-directory-api.php to tests/bootstrap.php require list — both files exist in includes/ but were missing from bootstrap, causing 5 PHPUnit "Class not found" failures in AIGeneratorTest and DirectoryTest.
This commit is contained in:
@@ -29,10 +29,14 @@ class PSA_Admin_Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wp_enqueue_script( 'psa-batch-enrich', PSA_PLUGIN_URL . 'assets/js/psa-batch-enrich.js', array( 'jquery' ), PSA_VERSION, true );
|
wp_enqueue_script( 'psa-batch-enrich', PSA_PLUGIN_URL . 'assets/js/psa-batch-enrich.js', array( 'jquery' ), PSA_VERSION, true );
|
||||||
wp_localize_script( 'psa-batch-enrich', 'psaBatchEnrich', array(
|
wp_localize_script(
|
||||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
'psa-batch-enrich',
|
||||||
'nonce' => wp_create_nonce( 'psa_batch_enrich' ),
|
'psaBatchEnrich',
|
||||||
) );
|
array(
|
||||||
|
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||||
|
'nonce' => wp_create_nonce( 'psa_batch_enrich' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Field renderers ─────────────────────────────────────────────────
|
// ── Field renderers ─────────────────────────────────────────────────
|
||||||
@@ -119,7 +123,11 @@ class PSA_Admin_Page {
|
|||||||
<h1><?php esc_html_e( 'Peptide Search AI Settings', 'peptide-search-ai' ); ?></h1>
|
<h1><?php esc_html_e( 'Peptide Search AI Settings', 'peptide-search-ai' ); ?></h1>
|
||||||
|
|
||||||
<form method="post" action="options.php">
|
<form method="post" action="options.php">
|
||||||
<?php settings_fields( 'psa_settings_group' ); do_settings_sections( 'peptide-search-ai' ); submit_button(); ?>
|
<?php
|
||||||
|
settings_fields( 'psa_settings_group' );
|
||||||
|
do_settings_sections( 'peptide-search-ai' );
|
||||||
|
submit_button();
|
||||||
|
?>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
@@ -168,12 +176,12 @@ class PSA_Admin_Page {
|
|||||||
|
|
||||||
/** Render the API usage summary section with recent call log. */
|
/** Render the API usage summary section with recent call log. */
|
||||||
private static function render_usage_summary(): void {
|
private static function render_usage_summary(): void {
|
||||||
$current_spend = PSA_Cost_Tracker::get_monthly_spend();
|
$current_spend = PSA_Cost_Tracker::get_monthly_spend();
|
||||||
$current_tokens = PSA_Cost_Tracker::get_monthly_tokens();
|
$current_tokens = PSA_Cost_Tracker::get_monthly_tokens();
|
||||||
$estimated_count = PSA_Cost_Tracker::get_monthly_estimated_count();
|
$estimated_count = PSA_Cost_Tracker::get_monthly_estimated_count();
|
||||||
$budget = floatval( PSA_Admin::get_setting( 'monthly_budget', PSA_Config::DEFAULT_MONTHLY_BUDGET ) );
|
$budget = floatval( PSA_Admin::get_setting( 'monthly_budget', PSA_Config::DEFAULT_MONTHLY_BUDGET ) );
|
||||||
$budget_remaining = ( 0.0 === $budget ) ? __( 'Unlimited', 'peptide-search-ai' ) : '$' . number_format( max( 0, $budget - $current_spend ), 2 );
|
$budget_remaining = ( 0.0 === $budget ) ? __( 'Unlimited', 'peptide-search-ai' ) : '$' . number_format( max( 0, $budget - $current_spend ), 2 );
|
||||||
$recent_logs = PSA_Cost_Tracker::get_recent_logs( 20 );
|
$recent_logs = PSA_Cost_Tracker::get_recent_logs( 20 );
|
||||||
?>
|
?>
|
||||||
<div style="background:#f5f5f5;padding:15px;border-radius:4px;margin-bottom:20px;">
|
<div style="background:#f5f5f5;padding:15px;border-radius:4px;margin-bottom:20px;">
|
||||||
<h3><?php esc_html_e( 'This Month', 'peptide-search-ai' ); ?></h3>
|
<h3><?php esc_html_e( 'This Month', 'peptide-search-ai' ); ?></h3>
|
||||||
@@ -216,6 +224,7 @@ class PSA_Admin_Page {
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<?php endif;
|
<?php
|
||||||
|
endif;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,13 @@ class PSA_Admin {
|
|||||||
|
|
||||||
/** Register settings fields — rendering delegated to PSA_Admin_Page. */
|
/** Register settings fields — rendering delegated to PSA_Admin_Page. */
|
||||||
public static function register_settings(): void {
|
public static function register_settings(): void {
|
||||||
register_setting( 'psa_settings_group', 'psa_settings', array(
|
register_setting(
|
||||||
'sanitize_callback' => array( __CLASS__, 'sanitize_settings' ),
|
'psa_settings_group',
|
||||||
) );
|
'psa_settings',
|
||||||
|
array(
|
||||||
|
'sanitize_callback' => array( __CLASS__, 'sanitize_settings' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
add_settings_section( 'psa_ai_section', __( 'OpenRouter API Settings', 'peptide-search-ai' ), array( 'PSA_Admin_Page', 'render_section_description' ), 'peptide-search-ai' );
|
add_settings_section( 'psa_ai_section', __( 'OpenRouter API Settings', 'peptide-search-ai' ), array( 'PSA_Admin_Page', 'render_section_description' ), 'peptide-search-ai' );
|
||||||
add_settings_field( 'api_key', __( 'API Key', 'peptide-search-ai' ), array( 'PSA_Admin_Page', 'render_api_key_field' ), 'peptide-search-ai', 'psa_ai_section' );
|
add_settings_field( 'api_key', __( 'API Key', 'peptide-search-ai' ), array( 'PSA_Admin_Page', 'render_api_key_field' ), 'peptide-search-ai', 'psa_ai_section' );
|
||||||
@@ -95,18 +99,27 @@ class PSA_Admin {
|
|||||||
|
|
||||||
if ( 'migrate_categories' === $action && wp_verify_nonce( $nonce, 'psa_migrate_categories' ) ) {
|
if ( 'migrate_categories' === $action && wp_verify_nonce( $nonce, 'psa_migrate_categories' ) ) {
|
||||||
self::run_category_migration();
|
self::run_category_migration();
|
||||||
add_action( 'admin_notices', function () {
|
add_action(
|
||||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Category migration completed.', 'peptide-search-ai' ) . '</p></div>';
|
'admin_notices',
|
||||||
} );
|
function () {
|
||||||
|
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Category migration completed.', 'peptide-search-ai' ) . '</p></div>';
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 'reenrich' === $action && wp_verify_nonce( $nonce, 'psa_reenrich' ) ) {
|
if ( 'reenrich' === $action && wp_verify_nonce( $nonce, 'psa_reenrich' ) ) {
|
||||||
$queued = self::queue_reenrichment();
|
$queued = self::queue_reenrichment();
|
||||||
add_action( 'admin_notices', function () use ( $queued ) {
|
add_action(
|
||||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( sprintf(
|
'admin_notices',
|
||||||
__( '%d peptides queued for re-enrichment.', 'peptide-search-ai' ), $queued
|
function () use ( $queued ) {
|
||||||
) ) . '</p></div>';
|
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html(
|
||||||
} );
|
sprintf(
|
||||||
|
__( '%d peptides queued for re-enrichment.', 'peptide-search-ai' ),
|
||||||
|
$queued
|
||||||
|
)
|
||||||
|
) . '</p></div>';
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,12 +130,14 @@ class PSA_Admin {
|
|||||||
* Idempotent — safe to run multiple times.
|
* Idempotent — safe to run multiple times.
|
||||||
*/
|
*/
|
||||||
private static function run_category_migration(): void {
|
private static function run_category_migration(): void {
|
||||||
$posts = get_posts( array(
|
$posts = get_posts(
|
||||||
'post_type' => 'peptide',
|
array(
|
||||||
'post_status' => array( 'publish', 'draft' ),
|
'post_type' => 'peptide',
|
||||||
'posts_per_page' => -1,
|
'post_status' => array( 'publish', 'draft' ),
|
||||||
'fields' => 'ids',
|
'posts_per_page' => -1,
|
||||||
) );
|
'fields' => 'ids',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
foreach ( $posts as $post_id ) {
|
foreach ( $posts as $post_id ) {
|
||||||
$terms = wp_get_object_terms( $post_id, 'peptide_category' );
|
$terms = wp_get_object_terms( $post_id, 'peptide_category' );
|
||||||
@@ -199,17 +214,25 @@ class PSA_Admin {
|
|||||||
* @return int Number of peptides queued.
|
* @return int Number of peptides queued.
|
||||||
*/
|
*/
|
||||||
private static function queue_reenrichment(): int {
|
private static function queue_reenrichment(): int {
|
||||||
$posts = get_posts( array(
|
$posts = get_posts(
|
||||||
'post_type' => 'peptide',
|
array(
|
||||||
'post_status' => 'publish',
|
'post_type' => 'peptide',
|
||||||
'posts_per_page' => -1,
|
'post_status' => 'publish',
|
||||||
'fields' => 'ids',
|
'posts_per_page' => -1,
|
||||||
'meta_query' => array(
|
'fields' => 'ids',
|
||||||
'relation' => 'OR',
|
'meta_query' => array(
|
||||||
array( 'key' => 'psa_half_life', 'compare' => 'NOT EXISTS' ),
|
'relation' => 'OR',
|
||||||
array( 'key' => 'psa_half_life', 'value' => '' ),
|
array(
|
||||||
),
|
'key' => 'psa_half_life',
|
||||||
) );
|
'compare' => 'NOT EXISTS',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'psa_half_life',
|
||||||
|
'value' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$daily_key = 'psa_daily_gen_' . gmdate( 'Y-m-d' );
|
$daily_key = 'psa_daily_gen_' . gmdate( 'Y-m-d' );
|
||||||
$daily_count = (int) get_transient( $daily_key );
|
$daily_count = (int) get_transient( $daily_key );
|
||||||
@@ -226,7 +249,12 @@ class PSA_Admin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_post_meta( $post_id, 'psa_source', 'pending' );
|
update_post_meta( $post_id, 'psa_source', 'pending' );
|
||||||
wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft' ) );
|
wp_update_post(
|
||||||
|
array(
|
||||||
|
'ID' => $post_id,
|
||||||
|
'post_status' => 'draft',
|
||||||
|
)
|
||||||
|
);
|
||||||
update_post_meta( $post_id, 'psa_generation_started', time() );
|
update_post_meta( $post_id, 'psa_generation_started', time() );
|
||||||
|
|
||||||
$args = array( $post_id, $post->post_title );
|
$args = array( $post_id, $post->post_title );
|
||||||
@@ -234,7 +262,7 @@ class PSA_Admin {
|
|||||||
// Stagger 30s apart to avoid rate-limit hammering.
|
// Stagger 30s apart to avoid rate-limit hammering.
|
||||||
wp_schedule_single_event( time() + ( $queued * 30 ), 'psa_generate_peptide_background', $args );
|
wp_schedule_single_event( time() + ( $queued * 30 ), 'psa_generate_peptide_background', $args );
|
||||||
}
|
}
|
||||||
$queued++;
|
++$queued;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_transient( $daily_key, $daily_count + $queued, DAY_IN_SECONDS );
|
set_transient( $daily_key, $daily_count + $queued, DAY_IN_SECONDS );
|
||||||
@@ -262,29 +290,38 @@ class PSA_Admin {
|
|||||||
// ── Backward-compatible proxies (rendering moved to PSA_Admin_Page) ─
|
// ── Backward-compatible proxies (rendering moved to PSA_Admin_Page) ─
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::enqueue_batch_scripts() */
|
/** @see PSA_Admin_Page::enqueue_batch_scripts() */
|
||||||
public static function enqueue_batch_scripts( string $hook ): void { PSA_Admin_Page::enqueue_batch_scripts( $hook ); }
|
public static function enqueue_batch_scripts( string $hook ): void {
|
||||||
|
PSA_Admin_Page::enqueue_batch_scripts( $hook ); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_section_description() */
|
/** @see PSA_Admin_Page::render_section_description() */
|
||||||
public static function render_section_description(): void { PSA_Admin_Page::render_section_description(); }
|
public static function render_section_description(): void {
|
||||||
|
PSA_Admin_Page::render_section_description(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_api_key_field() */
|
/** @see PSA_Admin_Page::render_api_key_field() */
|
||||||
public static function render_api_key_field(): void { PSA_Admin_Page::render_api_key_field(); }
|
public static function render_api_key_field(): void {
|
||||||
|
PSA_Admin_Page::render_api_key_field(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_model_field() */
|
/** @see PSA_Admin_Page::render_model_field() */
|
||||||
public static function render_model_field(): void { PSA_Admin_Page::render_model_field(); }
|
public static function render_model_field(): void {
|
||||||
|
PSA_Admin_Page::render_model_field(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_validation_model_field() */
|
/** @see PSA_Admin_Page::render_validation_model_field() */
|
||||||
public static function render_validation_model_field(): void { PSA_Admin_Page::render_validation_model_field(); }
|
public static function render_validation_model_field(): void {
|
||||||
|
PSA_Admin_Page::render_validation_model_field(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_publish_field() */
|
/** @see PSA_Admin_Page::render_publish_field() */
|
||||||
public static function render_publish_field(): void { PSA_Admin_Page::render_publish_field(); }
|
public static function render_publish_field(): void {
|
||||||
|
PSA_Admin_Page::render_publish_field(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_pubchem_field() */
|
/** @see PSA_Admin_Page::render_pubchem_field() */
|
||||||
public static function render_pubchem_field(): void { PSA_Admin_Page::render_pubchem_field(); }
|
public static function render_pubchem_field(): void {
|
||||||
|
PSA_Admin_Page::render_pubchem_field(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_budget_field() */
|
/** @see PSA_Admin_Page::render_budget_field() */
|
||||||
public static function render_budget_field(): void { PSA_Admin_Page::render_budget_field(); }
|
public static function render_budget_field(): void {
|
||||||
|
PSA_Admin_Page::render_budget_field(); }
|
||||||
|
|
||||||
/** @see PSA_Admin_Page::render_settings_page() */
|
/** @see PSA_Admin_Page::render_settings_page() */
|
||||||
public static function render_settings_page(): void { PSA_Admin_Page::render_settings_page(); }
|
public static function render_settings_page(): void {
|
||||||
|
PSA_Admin_Page::render_settings_page(); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ class PSA_AI_Generator {
|
|||||||
*/
|
*/
|
||||||
public static function validate_peptide_name( string $name ) {
|
public static function validate_peptide_name( string $name ) {
|
||||||
if ( ! self::validate_peptide_input( $name ) ) {
|
if ( ! self::validate_peptide_input( $name ) ) {
|
||||||
return array( 'is_valid' => false, 'canonical_name' => $name, 'reason' => 'Invalid characters in peptide name.' );
|
return array(
|
||||||
|
'is_valid' => false,
|
||||||
|
'canonical_name' => $name,
|
||||||
|
'reason' => 'Invalid characters in peptide name.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$options = self::get_settings();
|
$options = self::get_settings();
|
||||||
@@ -70,7 +74,11 @@ class PSA_AI_Generator {
|
|||||||
|
|
||||||
$data = PSA_OpenRouter::parse_response( $response );
|
$data = PSA_OpenRouter::parse_response( $response );
|
||||||
if ( is_wp_error( $data ) ) {
|
if ( is_wp_error( $data ) ) {
|
||||||
return array( 'is_valid' => false, 'canonical_name' => $name, 'reason' => 'Could not verify this peptide name.' );
|
return array(
|
||||||
|
'is_valid' => false,
|
||||||
|
'canonical_name' => $name,
|
||||||
|
'reason' => 'Could not verify this peptide name.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = array(
|
$result = array(
|
||||||
@@ -136,12 +144,15 @@ class PSA_AI_Generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$post_status = ( 'publish' === $options['auto_publish'] ) ? 'publish' : 'draft';
|
$post_status = ( 'publish' === $options['auto_publish'] ) ? 'publish' : 'draft';
|
||||||
$update_result = wp_update_post( array(
|
$update_result = wp_update_post(
|
||||||
'ID' => $post_id,
|
array(
|
||||||
'post_title' => sanitize_text_field( $result['name'] ?? $peptide_name ),
|
'ID' => $post_id,
|
||||||
'post_content' => wp_kses_post( $post_content ),
|
'post_title' => sanitize_text_field( $result['name'] ?? $peptide_name ),
|
||||||
'post_status' => $post_status,
|
'post_content' => wp_kses_post( $post_content ),
|
||||||
), true );
|
'post_status' => $post_status,
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
if ( is_wp_error( $update_result ) ) {
|
if ( is_wp_error( $update_result ) ) {
|
||||||
error_log( 'PSA: wp_update_post FAILED for post ' . $post_id . ': ' . $update_result->get_error_message() );
|
error_log( 'PSA: wp_update_post FAILED for post ' . $post_id . ': ' . $update_result->get_error_message() );
|
||||||
@@ -214,8 +225,14 @@ class PSA_AI_Generator {
|
|||||||
$gen_cost = PSA_Cost_Tracker::estimate_cost( $generation_model, $gen_tokens, PSA_Config::GENERATION_MAX_TOKENS );
|
$gen_cost = PSA_Cost_Tracker::estimate_cost( $generation_model, $gen_tokens, PSA_Config::GENERATION_MAX_TOKENS );
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'validation' => array( 'tokens' => $val_tokens + PSA_Config::VALIDATION_MAX_TOKENS, 'estimated_cost_usd' => $val_cost ),
|
'validation' => array(
|
||||||
'generation' => array( 'tokens' => $gen_tokens + PSA_Config::GENERATION_MAX_TOKENS, 'estimated_cost_usd' => $gen_cost ),
|
'tokens' => $val_tokens + PSA_Config::VALIDATION_MAX_TOKENS,
|
||||||
|
'estimated_cost_usd' => $val_cost,
|
||||||
|
),
|
||||||
|
'generation' => array(
|
||||||
|
'tokens' => $gen_tokens + PSA_Config::GENERATION_MAX_TOKENS,
|
||||||
|
'estimated_cost_usd' => $gen_cost,
|
||||||
|
),
|
||||||
'total_estimated_cost_usd' => $val_cost + $gen_cost,
|
'total_estimated_cost_usd' => $val_cost + $gen_cost,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -228,7 +245,13 @@ class PSA_AI_Generator {
|
|||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private static function get_settings(): array {
|
private static function get_settings(): array {
|
||||||
$defaults = array( 'api_key' => '', 'ai_model' => '', 'validation_model' => '', 'auto_publish' => 'draft', 'use_pubchem' => '1' );
|
$defaults = array(
|
||||||
|
'api_key' => '',
|
||||||
|
'ai_model' => '',
|
||||||
|
'validation_model' => '',
|
||||||
|
'auto_publish' => 'draft',
|
||||||
|
'use_pubchem' => '1',
|
||||||
|
);
|
||||||
$settings = wp_parse_args( get_option( 'psa_settings', array() ), $defaults );
|
$settings = wp_parse_args( get_option( 'psa_settings', array() ), $defaults );
|
||||||
|
|
||||||
if ( ! empty( $settings['api_key'] ) && ! ( defined( 'PSA_OPENROUTER_KEY' ) && PSA_OPENROUTER_KEY ) ) {
|
if ( ! empty( $settings['api_key'] ) && ! ( defined( 'PSA_OPENROUTER_KEY' ) && PSA_OPENROUTER_KEY ) ) {
|
||||||
@@ -269,14 +292,18 @@ class PSA_AI_Generator {
|
|||||||
// ── Backward-compatible proxies ─────────────────────────────────────
|
// ── Backward-compatible proxies ─────────────────────────────────────
|
||||||
|
|
||||||
/** @see PSA_AI_Content::build_validation_prompt() */
|
/** @see PSA_AI_Content::build_validation_prompt() */
|
||||||
public static function build_validation_prompt( string $name ): string { return PSA_AI_Content::build_validation_prompt( $name ); }
|
public static function build_validation_prompt( string $name ): string {
|
||||||
|
return PSA_AI_Content::build_validation_prompt( $name ); }
|
||||||
|
|
||||||
/** @see PSA_AI_Content::build_generation_prompt() */
|
/** @see PSA_AI_Content::build_generation_prompt() */
|
||||||
public static function build_generation_prompt( string $peptide_name ): string { return PSA_AI_Content::build_generation_prompt( $peptide_name ); }
|
public static function build_generation_prompt( string $peptide_name ): string {
|
||||||
|
return PSA_AI_Content::build_generation_prompt( $peptide_name ); }
|
||||||
|
|
||||||
/** @see PSA_AI_Content::save_peptide_meta() */
|
/** @see PSA_AI_Content::save_peptide_meta() */
|
||||||
public static function save_peptide_meta( int $post_id, array $ai_data, ?array $pubchem_data ): void { PSA_AI_Content::save_peptide_meta( $post_id, $ai_data, $pubchem_data ); }
|
public static function save_peptide_meta( int $post_id, array $ai_data, ?array $pubchem_data ): void {
|
||||||
|
PSA_AI_Content::save_peptide_meta( $post_id, $ai_data, $pubchem_data ); }
|
||||||
|
|
||||||
/** @see PSA_AI_Content::assign_category_term() */
|
/** @see PSA_AI_Content::assign_category_term() */
|
||||||
public static function assign_category_term( int $post_id, array $ai_data ): void { PSA_AI_Content::assign_category_term( $post_id, $ai_data ); }
|
public static function assign_category_term( int $post_id, array $ai_data ): void {
|
||||||
|
PSA_AI_Content::assign_category_term( $post_id, $ai_data ); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class PSA_Batch_Enrichment {
|
|||||||
PSA_AI_Generator::background_generate( $post_id, $peptide_name );
|
PSA_AI_Generator::background_generate( $post_id, $peptide_name );
|
||||||
|
|
||||||
// Check outcome: did generation succeed?
|
// Check outcome: did generation succeed?
|
||||||
$error = get_post_meta( $post_id, 'psa_generation_error', true );
|
$error = get_post_meta( $post_id, 'psa_generation_error', true );
|
||||||
$refreshed_post = get_post( $post_id );
|
$refreshed_post = get_post( $post_id );
|
||||||
|
|
||||||
// If still in draft (generation failed or auto_publish is 'draft'), force republish.
|
// If still in draft (generation failed or auto_publish is 'draft'), force republish.
|
||||||
|
|||||||
@@ -25,23 +25,23 @@ class PSA_Config {
|
|||||||
const AJAX_RATE_LIMIT = 10;
|
const AJAX_RATE_LIMIT = 10;
|
||||||
|
|
||||||
// Daily generation cap
|
// Daily generation cap
|
||||||
const DAILY_GENERATION_CAP = 50;
|
const DAILY_GENERATION_CAP = 50;
|
||||||
|
|
||||||
// Generation timing
|
// Generation timing
|
||||||
const PENDING_TIMEOUT = 300; // seconds (5 minutes)
|
const PENDING_TIMEOUT = 300; // seconds (5 minutes)
|
||||||
const MAX_GENERATION_RETRIES = 3;
|
const MAX_GENERATION_RETRIES = 3;
|
||||||
|
|
||||||
// AI token limits
|
// AI token limits
|
||||||
const VALIDATION_MAX_TOKENS = 300;
|
const VALIDATION_MAX_TOKENS = 300;
|
||||||
const GENERATION_MAX_TOKENS = 4096;
|
const GENERATION_MAX_TOKENS = 4096;
|
||||||
|
|
||||||
// Cache TTL
|
// Cache TTL
|
||||||
const VALIDATION_CACHE_TTL = null; // Set to DAY_IN_SECONDS at runtime
|
const VALIDATION_CACHE_TTL = null; // Set to DAY_IN_SECONDS at runtime
|
||||||
const PUBCHEM_CACHE_TTL = null; // Set to 7 * DAY_IN_SECONDS at runtime
|
const PUBCHEM_CACHE_TTL = null; // Set to 7 * DAY_IN_SECONDS at runtime
|
||||||
|
|
||||||
// API retry settings
|
// API retry settings
|
||||||
const API_RETRY_MAX = 3;
|
const API_RETRY_MAX = 3;
|
||||||
const API_RETRY_BASE_DELAY = 5; // seconds
|
const API_RETRY_BASE_DELAY = 5; // seconds
|
||||||
|
|
||||||
// Cost tracking and budget
|
// Cost tracking and budget
|
||||||
const DEFAULT_MONTHLY_BUDGET = 0; // 0 = unlimited
|
const DEFAULT_MONTHLY_BUDGET = 0; // 0 = unlimited
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class PSA_Cost_Reporter {
|
|||||||
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float {
|
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
list( $start, $end ) = self::month_range( $year, $month );
|
list( $start, $end ) = self::month_range( $year, $month );
|
||||||
$table = $wpdb->prefix . 'psa_api_logs';
|
$table = $wpdb->prefix . 'psa_api_logs';
|
||||||
|
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||||
$result = $wpdb->get_var(
|
$result = $wpdb->get_var(
|
||||||
@@ -53,7 +53,7 @@ class PSA_Cost_Reporter {
|
|||||||
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int {
|
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
list( $start, $end ) = self::month_range( $year, $month );
|
list( $start, $end ) = self::month_range( $year, $month );
|
||||||
$table = $wpdb->prefix . 'psa_api_logs';
|
$table = $wpdb->prefix . 'psa_api_logs';
|
||||||
|
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||||
$result = $wpdb->get_var(
|
$result = $wpdb->get_var(
|
||||||
@@ -80,7 +80,7 @@ class PSA_Cost_Reporter {
|
|||||||
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int {
|
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
list( $start, $end ) = self::month_range( $year, $month );
|
list( $start, $end ) = self::month_range( $year, $month );
|
||||||
$table = $wpdb->prefix . 'psa_api_logs';
|
$table = $wpdb->prefix . 'psa_api_logs';
|
||||||
|
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||||
$result = $wpdb->get_var(
|
$result = $wpdb->get_var(
|
||||||
|
|||||||
@@ -158,14 +158,18 @@ class PSA_Cost_Tracker {
|
|||||||
// ── Backward-compatible proxies (reporting moved to PSA_Cost_Reporter) ──
|
// ── Backward-compatible proxies (reporting moved to PSA_Cost_Reporter) ──
|
||||||
|
|
||||||
/** @see PSA_Cost_Reporter::get_monthly_spend() */
|
/** @see PSA_Cost_Reporter::get_monthly_spend() */
|
||||||
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float { return PSA_Cost_Reporter::get_monthly_spend( $year, $month ); }
|
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float {
|
||||||
|
return PSA_Cost_Reporter::get_monthly_spend( $year, $month ); }
|
||||||
|
|
||||||
/** @see PSA_Cost_Reporter::get_monthly_tokens() */
|
/** @see PSA_Cost_Reporter::get_monthly_tokens() */
|
||||||
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int { return PSA_Cost_Reporter::get_monthly_tokens( $year, $month ); }
|
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int {
|
||||||
|
return PSA_Cost_Reporter::get_monthly_tokens( $year, $month ); }
|
||||||
|
|
||||||
/** @see PSA_Cost_Reporter::get_monthly_estimated_count() */
|
/** @see PSA_Cost_Reporter::get_monthly_estimated_count() */
|
||||||
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int { return PSA_Cost_Reporter::get_monthly_estimated_count( $year, $month ); }
|
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int {
|
||||||
|
return PSA_Cost_Reporter::get_monthly_estimated_count( $year, $month ); }
|
||||||
|
|
||||||
/** @see PSA_Cost_Reporter::get_recent_logs() */
|
/** @see PSA_Cost_Reporter::get_recent_logs() */
|
||||||
public static function get_recent_logs( int $limit = 20 ): array { return PSA_Cost_Reporter::get_recent_logs( $limit ); }
|
public static function get_recent_logs( int $limit = 20 ): array {
|
||||||
|
return PSA_Cost_Reporter::get_recent_logs( $limit ); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,18 +23,45 @@ class PSA_Directory_API {
|
|||||||
* Register the /v1/compounds endpoint. Public (no auth) — rate limiting at server/CDN level.
|
* Register the /v1/compounds endpoint. Public (no auth) — rate limiting at server/CDN level.
|
||||||
*/
|
*/
|
||||||
public static function register_rest_routes(): void {
|
public static function register_rest_routes(): void {
|
||||||
register_rest_route( 'peptide-search-ai/v1', '/compounds', array(
|
register_rest_route(
|
||||||
'methods' => 'GET',
|
'peptide-search-ai/v1',
|
||||||
'callback' => array( __CLASS__, 'rest_compounds' ),
|
'/compounds',
|
||||||
'permission_callback' => '__return_true',
|
array(
|
||||||
'args' => array(
|
'methods' => 'GET',
|
||||||
'search' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ),
|
'callback' => array( __CLASS__, 'rest_compounds' ),
|
||||||
'category' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ),
|
'permission_callback' => '__return_true',
|
||||||
'fields' => array( 'type' => 'string', 'enum' => array( 'basic', 'full' ), 'default' => 'full' ),
|
'args' => array(
|
||||||
'page' => array( 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'sanitize_callback' => 'absint' ),
|
'search' => array(
|
||||||
'per_page' => array( 'type' => 'integer', 'default' => PSA_Directory::PER_PAGE_DEFAULT, 'minimum' => 1, 'maximum' => PSA_Directory::PER_PAGE_MAX, 'sanitize_callback' => 'absint' ),
|
'type' => 'string',
|
||||||
),
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
) );
|
'default' => '',
|
||||||
|
),
|
||||||
|
'category' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
|
'default' => '',
|
||||||
|
),
|
||||||
|
'fields' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array( 'basic', 'full' ),
|
||||||
|
'default' => 'full',
|
||||||
|
),
|
||||||
|
'page' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'minimum' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
),
|
||||||
|
'per_page' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => PSA_Directory::PER_PAGE_DEFAULT,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => PSA_Directory::PER_PAGE_MAX,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,11 +92,13 @@ class PSA_Directory_API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( ! empty( $category ) ) {
|
if ( ! empty( $category ) ) {
|
||||||
$query_args['tax_query'] = array( array(
|
$query_args['tax_query'] = array(
|
||||||
'taxonomy' => 'peptide_category',
|
array(
|
||||||
'field' => 'slug',
|
'taxonomy' => 'peptide_category',
|
||||||
'terms' => $category,
|
'field' => 'slug',
|
||||||
) );
|
'terms' => $category,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = new WP_Query( $query_args );
|
$query = new WP_Query( $query_args );
|
||||||
@@ -83,13 +112,16 @@ class PSA_Directory_API {
|
|||||||
$compounds[] = self::format_compound( $post, $fields );
|
$compounds[] = self::format_compound( $post, $fields );
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = new WP_REST_Response( array(
|
$response = new WP_REST_Response(
|
||||||
'compounds' => $compounds,
|
array(
|
||||||
'total' => (int) $query->found_posts,
|
'compounds' => $compounds,
|
||||||
'total_pages' => (int) $query->max_num_pages,
|
'total' => (int) $query->found_posts,
|
||||||
'page' => $page,
|
'total_pages' => (int) $query->max_num_pages,
|
||||||
'per_page' => $per_page,
|
'page' => $page,
|
||||||
), 200 );
|
'per_page' => $per_page,
|
||||||
|
),
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
$response->header( 'X-WP-Total', (string) $query->found_posts );
|
$response->header( 'X-WP-Total', (string) $query->found_posts );
|
||||||
$response->header( 'X-WP-TotalPages', (string) $query->max_num_pages );
|
$response->header( 'X-WP-TotalPages', (string) $query->max_num_pages );
|
||||||
@@ -111,7 +143,10 @@ class PSA_Directory_API {
|
|||||||
$categories = array();
|
$categories = array();
|
||||||
if ( ! is_wp_error( $terms ) ) {
|
if ( ! is_wp_error( $terms ) ) {
|
||||||
foreach ( $terms as $term ) {
|
foreach ( $terms as $term ) {
|
||||||
$categories[] = array( 'slug' => $term->slug, 'name' => $term->name );
|
$categories[] = array(
|
||||||
|
'slug' => $term->slug,
|
||||||
|
'name' => $term->name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,27 +167,30 @@ class PSA_Directory_API {
|
|||||||
return $basic;
|
return $basic;
|
||||||
}
|
}
|
||||||
|
|
||||||
$full = array_merge( $basic, array(
|
$full = array_merge(
|
||||||
'excerpt' => wp_trim_words( $post->post_content, 30 ),
|
$basic,
|
||||||
'description' => wp_trim_words( $post->post_content, 80 ),
|
array(
|
||||||
'sequence' => get_post_meta( $id, 'psa_sequence', true ),
|
'excerpt' => wp_trim_words( $post->post_content, 30 ),
|
||||||
'molecular_weight' => get_post_meta( $id, 'psa_molecular_weight', true ),
|
'description' => wp_trim_words( $post->post_content, 80 ),
|
||||||
'molecular_formula' => get_post_meta( $id, 'psa_molecular_formula', true ),
|
'sequence' => get_post_meta( $id, 'psa_sequence', true ),
|
||||||
'aliases' => get_post_meta( $id, 'psa_aliases', true ),
|
'molecular_weight' => get_post_meta( $id, 'psa_molecular_weight', true ),
|
||||||
'mechanism' => get_post_meta( $id, 'psa_mechanism', true ),
|
'molecular_formula' => get_post_meta( $id, 'psa_molecular_formula', true ),
|
||||||
'research_apps' => get_post_meta( $id, 'psa_research_apps', true ),
|
'aliases' => get_post_meta( $id, 'psa_aliases', true ),
|
||||||
'safety_profile' => get_post_meta( $id, 'psa_safety_profile', true ),
|
'mechanism' => get_post_meta( $id, 'psa_mechanism', true ),
|
||||||
'dosage_info' => get_post_meta( $id, 'psa_dosage_info', true ),
|
'research_apps' => get_post_meta( $id, 'psa_research_apps', true ),
|
||||||
'references' => get_post_meta( $id, 'psa_references', true ),
|
'safety_profile' => get_post_meta( $id, 'psa_safety_profile', true ),
|
||||||
'pubchem_cid' => get_post_meta( $id, 'psa_pubchem_cid', true ),
|
'dosage_info' => get_post_meta( $id, 'psa_dosage_info', true ),
|
||||||
'solubility' => get_post_meta( $id, 'psa_solubility', true ),
|
'references' => get_post_meta( $id, 'psa_references', true ),
|
||||||
'vial_size_mg' => get_post_meta( $id, 'psa_vial_size_mg', true ),
|
'pubchem_cid' => get_post_meta( $id, 'psa_pubchem_cid', true ),
|
||||||
'storage_lyophilized' => get_post_meta( $id, 'psa_storage_lyophilized', true ),
|
'solubility' => get_post_meta( $id, 'psa_solubility', true ),
|
||||||
'storage_reconstituted' => get_post_meta( $id, 'psa_storage_reconstituted', true ),
|
'vial_size_mg' => get_post_meta( $id, 'psa_vial_size_mg', true ),
|
||||||
'typical_dose_mcg' => get_post_meta( $id, 'psa_typical_dose_mcg', true ),
|
'storage_lyophilized' => get_post_meta( $id, 'psa_storage_lyophilized', true ),
|
||||||
'cycle_parameters' => get_post_meta( $id, 'psa_cycle_parameters', true ),
|
'storage_reconstituted' => get_post_meta( $id, 'psa_storage_reconstituted', true ),
|
||||||
'amino_acid_count' => get_post_meta( $id, 'psa_amino_acid_count', true ),
|
'typical_dose_mcg' => get_post_meta( $id, 'psa_typical_dose_mcg', true ),
|
||||||
) );
|
'cycle_parameters' => get_post_meta( $id, 'psa_cycle_parameters', true ),
|
||||||
|
'amino_acid_count' => get_post_meta( $id, 'psa_amino_acid_count', true ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return apply_filters( 'psa_rest_compound_data', $full, $id );
|
return apply_filters( 'psa_rest_compound_data', $full, $id );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,57 +38,66 @@ class PSA_Directory {
|
|||||||
* @return string HTML output.
|
* @return string HTML output.
|
||||||
*/
|
*/
|
||||||
public static function render_shortcode( $atts ): string {
|
public static function render_shortcode( $atts ): string {
|
||||||
$terms = get_terms( array(
|
$terms = get_terms(
|
||||||
'taxonomy' => 'peptide_category',
|
array(
|
||||||
'hide_empty' => false,
|
'taxonomy' => 'peptide_category',
|
||||||
'orderby' => 'name',
|
'hide_empty' => false,
|
||||||
'order' => 'ASC',
|
'orderby' => 'name',
|
||||||
) );
|
'order' => 'ASC',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$categories = array();
|
$categories = array();
|
||||||
if ( ! is_wp_error( $terms ) ) {
|
if ( ! is_wp_error( $terms ) ) {
|
||||||
foreach ( $terms as $term ) {
|
foreach ( $terms as $term ) {
|
||||||
$categories[] = array( 'slug' => $term->slug, 'name' => $term->name );
|
$categories[] = array(
|
||||||
|
'slug' => $term->slug,
|
||||||
|
'name' => $term->name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_localize_script( 'psa-directory', 'psaDirectory', array(
|
wp_localize_script(
|
||||||
'restUrl' => esc_url_raw( rest_url( 'peptide-search-ai/v1/compounds' ) ),
|
'psa-directory',
|
||||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
'psaDirectory',
|
||||||
'categories' => $categories,
|
array(
|
||||||
'perPage' => self::PER_PAGE_DEFAULT,
|
'restUrl' => esc_url_raw( rest_url( 'peptide-search-ai/v1/compounds' ) ),
|
||||||
'i18n' => array(
|
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||||
'title' => __( 'Peptide Directory', 'peptide-search-ai' ),
|
'categories' => $categories,
|
||||||
'subtitle' => __( 'Browse reagents with detailed research parameters.', 'peptide-search-ai' ),
|
'perPage' => self::PER_PAGE_DEFAULT,
|
||||||
'searchPlaceholder' => __( 'Search peptides...', 'peptide-search-ai' ),
|
'i18n' => array(
|
||||||
'showAll' => __( 'Show All', 'peptide-search-ai' ),
|
'title' => __( 'Peptide Directory', 'peptide-search-ai' ),
|
||||||
'loadMore' => __( 'Load More', 'peptide-search-ai' ),
|
'subtitle' => __( 'Browse reagents with detailed research parameters.', 'peptide-search-ai' ),
|
||||||
'loading' => __( 'Loading...', 'peptide-search-ai' ),
|
'searchPlaceholder' => __( 'Search peptides...', 'peptide-search-ai' ),
|
||||||
'noResults' => __( 'No peptides found matching your criteria.', 'peptide-search-ai' ),
|
'showAll' => __( 'Show All', 'peptide-search-ai' ),
|
||||||
'viewDetails' => __( 'View Research Details', 'peptide-search-ai' ),
|
'loadMore' => __( 'Load More', 'peptide-search-ai' ),
|
||||||
'viewFullPage' => __( 'View Full Page', 'peptide-search-ai' ),
|
'loading' => __( 'Loading...', 'peptide-search-ai' ),
|
||||||
'close' => __( 'Close', 'peptide-search-ai' ),
|
'noResults' => __( 'No peptides found matching your criteria.', 'peptide-search-ai' ),
|
||||||
'halfLife' => __( 'Half-Life', 'peptide-search-ai' ),
|
'viewDetails' => __( 'View Research Details', 'peptide-search-ai' ),
|
||||||
'stability' => __( 'Stability', 'peptide-search-ai' ),
|
'viewFullPage' => __( 'View Full Page', 'peptide-search-ai' ),
|
||||||
'solubility' => __( 'Solubility', 'peptide-search-ai' ),
|
'close' => __( 'Close', 'peptide-search-ai' ),
|
||||||
'vialSize' => __( 'Vial Size', 'peptide-search-ai' ),
|
'halfLife' => __( 'Half-Life', 'peptide-search-ai' ),
|
||||||
'storageLyo' => __( 'Storage (Lyophilized)', 'peptide-search-ai' ),
|
'stability' => __( 'Stability', 'peptide-search-ai' ),
|
||||||
'storageRecon' => __( 'Storage (Reconstituted)', 'peptide-search-ai' ),
|
'solubility' => __( 'Solubility', 'peptide-search-ai' ),
|
||||||
'molecularWeight' => __( 'Molecular Weight', 'peptide-search-ai' ),
|
'vialSize' => __( 'Vial Size', 'peptide-search-ai' ),
|
||||||
'formula' => __( 'Formula', 'peptide-search-ai' ),
|
'storageLyo' => __( 'Storage (Lyophilized)', 'peptide-search-ai' ),
|
||||||
'typicalDose' => __( 'Typical Dose', 'peptide-search-ai' ),
|
'storageRecon' => __( 'Storage (Reconstituted)', 'peptide-search-ai' ),
|
||||||
'cycleParams' => __( 'Cycle Parameters', 'peptide-search-ai' ),
|
'molecularWeight' => __( 'Molecular Weight', 'peptide-search-ai' ),
|
||||||
'sequence' => __( 'Sequence', 'peptide-search-ai' ),
|
'formula' => __( 'Formula', 'peptide-search-ai' ),
|
||||||
'singleReagents' => __( 'Single Reagents', 'peptide-search-ai' ),
|
'typicalDose' => __( 'Typical Dose', 'peptide-search-ai' ),
|
||||||
'protocolModels' => __( 'Protocol Models', 'peptide-search-ai' ),
|
'cycleParams' => __( 'Cycle Parameters', 'peptide-search-ai' ),
|
||||||
'comingSoon' => __( 'Coming Soon', 'peptide-search-ai' ),
|
'sequence' => __( 'Sequence', 'peptide-search-ai' ),
|
||||||
'researchParams' => __( 'Research Parameters', 'peptide-search-ai' ),
|
'singleReagents' => __( 'Single Reagents', 'peptide-search-ai' ),
|
||||||
'communityObs' => __( 'Community Observations', 'peptide-search-ai' ),
|
'protocolModels' => __( 'Protocol Models', 'peptide-search-ai' ),
|
||||||
'pubchemLink' => __( 'View on PubChem', 'peptide-search-ai' ),
|
'comingSoon' => __( 'Coming Soon', 'peptide-search-ai' ),
|
||||||
'copySequence' => __( 'Copy', 'peptide-search-ai' ),
|
'researchParams' => __( 'Research Parameters', 'peptide-search-ai' ),
|
||||||
'copied' => __( 'Copied!', 'peptide-search-ai' ),
|
'communityObs' => __( 'Community Observations', 'peptide-search-ai' ),
|
||||||
),
|
'pubchemLink' => __( 'View on PubChem', 'peptide-search-ai' ),
|
||||||
) );
|
'copySequence' => __( 'Copy', 'peptide-search-ai' ),
|
||||||
|
'copied' => __( 'Copied!', 'peptide-search-ai' ),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
?>
|
?>
|
||||||
@@ -104,11 +113,14 @@ class PSA_Directory {
|
|||||||
// ── Backward-compatible proxies (REST moved to PSA_Directory_API) ───
|
// ── Backward-compatible proxies (REST moved to PSA_Directory_API) ───
|
||||||
|
|
||||||
/** @see PSA_Directory_API::register_rest_routes() */
|
/** @see PSA_Directory_API::register_rest_routes() */
|
||||||
public static function register_rest_routes(): void { PSA_Directory_API::register_rest_routes(); }
|
public static function register_rest_routes(): void {
|
||||||
|
PSA_Directory_API::register_rest_routes(); }
|
||||||
|
|
||||||
/** @see PSA_Directory_API::rest_compounds() */
|
/** @see PSA_Directory_API::rest_compounds() */
|
||||||
public static function rest_compounds( $request ) { return PSA_Directory_API::rest_compounds( $request ); }
|
public static function rest_compounds( $request ) {
|
||||||
|
return PSA_Directory_API::rest_compounds( $request ); }
|
||||||
|
|
||||||
/** @see PSA_Directory_API::format_compound() */
|
/** @see PSA_Directory_API::format_compound() */
|
||||||
public static function format_compound( $post, string $fields = 'full' ): array { return PSA_Directory_API::format_compound( $post, $fields ); }
|
public static function format_compound( $post, string $fields = 'full' ): array {
|
||||||
|
return PSA_Directory_API::format_compound( $post, $fields ); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class PSA_Encryption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First 16 bytes are the IV.
|
// First 16 bytes are the IV.
|
||||||
$iv = substr( $data, 0, 16 );
|
$iv = substr( $data, 0, 16 );
|
||||||
$encrypted_data = substr( $data, 16 );
|
$encrypted_data = substr( $data, 16 );
|
||||||
|
|
||||||
// Decrypt.
|
// Decrypt.
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class PSA_KB_Builder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$title = sanitize_text_field( $ai_data['name'] ?? $peptide_name );
|
$title = sanitize_text_field( $ai_data['name'] ?? $peptide_name );
|
||||||
$existing = get_posts(
|
$existing = get_posts(
|
||||||
array(
|
array(
|
||||||
'post_type' => 'epkb_post_type_1',
|
'post_type' => 'epkb_post_type_1',
|
||||||
@@ -124,10 +124,10 @@ class PSA_KB_Builder {
|
|||||||
|
|
||||||
// Research & Potential Benefits (with h3 subheadings).
|
// Research & Potential Benefits (with h3 subheadings).
|
||||||
if ( ! empty( $ai_data['research_benefits'] ) || ! empty( $ai_data['research_applications'] ) ) {
|
if ( ! empty( $ai_data['research_benefits'] ) || ! empty( $ai_data['research_applications'] ) ) {
|
||||||
$c .= '<h2>Research & Potential Benefits</h2>' . "\n";
|
$c .= '<h2>Research & Potential Benefits</h2>' . "\n";
|
||||||
$c .= '<p><em>The following potential benefits are based on animal studies and preclinical research. Human clinical trial data is extremely limited.</em></p>' . "\n";
|
$c .= '<p><em>The following potential benefits are based on animal studies and preclinical research. Human clinical trial data is extremely limited.</em></p>' . "\n";
|
||||||
$research = $ai_data['research_benefits'] ?? $ai_data['research_applications'] ?? '';
|
$research = $ai_data['research_benefits'] ?? $ai_data['research_applications'] ?? '';
|
||||||
$c .= self::research_with_subheadings( $research );
|
$c .= self::research_with_subheadings( $research );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Administration & Dosing.
|
// Administration & Dosing.
|
||||||
|
|||||||
@@ -90,15 +90,15 @@ class PSA_OpenRouter {
|
|||||||
array(
|
array(
|
||||||
'timeout' => 120,
|
'timeout' => 120,
|
||||||
'headers' => array(
|
'headers' => array(
|
||||||
'Content-Type' => 'application/json',
|
'Content-Type' => 'application/json',
|
||||||
'Authorization' => 'Bearer ' . $api_key,
|
'Authorization' => 'Bearer ' . $api_key,
|
||||||
'HTTP-Referer' => $site_url,
|
'HTTP-Referer' => $site_url,
|
||||||
'X-Title' => $site_name,
|
'X-Title' => $site_name,
|
||||||
),
|
),
|
||||||
'body' => wp_json_encode(
|
'body' => wp_json_encode(
|
||||||
array(
|
array(
|
||||||
'model' => $model,
|
'model' => $model,
|
||||||
'messages' => array(
|
'messages' => array(
|
||||||
array(
|
array(
|
||||||
'role' => 'system',
|
'role' => 'system',
|
||||||
'content' => 'You are a scientific database assistant. Return only valid JSON.',
|
'content' => 'You are a scientific database assistant. Return only valid JSON.',
|
||||||
@@ -108,8 +108,8 @@ class PSA_OpenRouter {
|
|||||||
'content' => $prompt,
|
'content' => $prompt,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'max_tokens' => $max_tokens,
|
'max_tokens' => $max_tokens,
|
||||||
'temperature' => 0.3,
|
'temperature' => 0.3,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -147,10 +147,10 @@ class PSA_OpenRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log API call with token usage.
|
// Log API call with token usage.
|
||||||
$response_text = $body['choices'][0]['message']['content'];
|
$response_text = $body['choices'][0]['message']['content'];
|
||||||
$prompt_tokens = (int) ( $body['usage']['prompt_tokens'] ?? 0 );
|
$prompt_tokens = (int) ( $body['usage']['prompt_tokens'] ?? 0 );
|
||||||
$completion_tokens = (int) ( $body['usage']['completion_tokens'] ?? 0 );
|
$completion_tokens = (int) ( $body['usage']['completion_tokens'] ?? 0 );
|
||||||
$token_source = 'api';
|
$token_source = 'api';
|
||||||
|
|
||||||
// Diagnostic: log the raw usage structure once per model so we can verify
|
// Diagnostic: log the raw usage structure once per model so we can verify
|
||||||
// whether the provider returns token counts. Remove after confirming.
|
// whether the provider returns token counts. Remove after confirming.
|
||||||
@@ -172,14 +172,14 @@ class PSA_OpenRouter {
|
|||||||
|
|
||||||
PSA_Cost_Tracker::log_api_call(
|
PSA_Cost_Tracker::log_api_call(
|
||||||
array(
|
array(
|
||||||
'provider' => 'openrouter',
|
'provider' => 'openrouter',
|
||||||
'model' => $model,
|
'model' => $model,
|
||||||
'prompt_tokens' => $prompt_tokens,
|
'prompt_tokens' => $prompt_tokens,
|
||||||
'completion_tokens' => $completion_tokens,
|
'completion_tokens' => $completion_tokens,
|
||||||
'request_type' => $request_type,
|
'request_type' => $request_type,
|
||||||
'peptide_name' => $peptide_name,
|
'peptide_name' => $peptide_name,
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'token_source' => $token_source,
|
'token_source' => $token_source,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,12 @@ class PSA_Post_Type_Meta {
|
|||||||
wp_nonce_field( 'psa_save_meta', 'psa_meta_nonce' );
|
wp_nonce_field( 'psa_save_meta', 'psa_meta_nonce' );
|
||||||
|
|
||||||
$textarea_fields = array(
|
$textarea_fields = array(
|
||||||
'psa_sequence', 'psa_mechanism', 'psa_research_apps',
|
'psa_sequence',
|
||||||
'psa_safety_profile', 'psa_dosage_info', 'psa_references',
|
'psa_mechanism',
|
||||||
|
'psa_research_apps',
|
||||||
|
'psa_safety_profile',
|
||||||
|
'psa_dosage_info',
|
||||||
|
'psa_references',
|
||||||
);
|
);
|
||||||
|
|
||||||
echo '<table class="form-table">';
|
echo '<table class="form-table">';
|
||||||
@@ -60,8 +64,11 @@ class PSA_Post_Type_Meta {
|
|||||||
|
|
||||||
if ( 'psa_source' === $key ) {
|
if ( 'psa_source' === $key ) {
|
||||||
$options = array(
|
$options = array(
|
||||||
'' => '— Select —', 'ai-generated' => 'AI Generated',
|
'' => '— Select —',
|
||||||
'pubchem' => 'PubChem Verified', 'manual' => 'Manual Entry', 'pending' => 'Pending Generation',
|
'ai-generated' => 'AI Generated',
|
||||||
|
'pubchem' => 'PubChem Verified',
|
||||||
|
'manual' => 'Manual Entry',
|
||||||
|
'pending' => 'Pending Generation',
|
||||||
);
|
);
|
||||||
echo '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $key ) . '">';
|
echo '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $key ) . '">';
|
||||||
foreach ( $options as $opt_val => $opt_label ) {
|
foreach ( $options as $opt_val => $opt_label ) {
|
||||||
@@ -113,7 +120,7 @@ class PSA_Post_Type_Meta {
|
|||||||
public static function save_meta( int $post_id ): void {
|
public static function save_meta( int $post_id ): void {
|
||||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||||
if ( ! isset( $_POST['psa_meta_nonce'] ) ||
|
if ( ! isset( $_POST['psa_meta_nonce'] ) ||
|
||||||
! wp_verify_nonce( wp_unslash( $_POST['psa_meta_nonce'] ), 'psa_save_meta' ) ) {
|
! wp_verify_nonce( wp_unslash( $_POST['psa_meta_nonce'] ), 'psa_save_meta' ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||||
|
|||||||
@@ -68,15 +68,15 @@ class PSA_Post_Type {
|
|||||||
|
|
||||||
/** Extended meta fields added in v4.3.0 for the directory feature. */
|
/** Extended meta fields added in v4.3.0 for the directory feature. */
|
||||||
const EXTENDED_META_FIELDS = array(
|
const EXTENDED_META_FIELDS = array(
|
||||||
'psa_half_life' => 'Half-Life',
|
'psa_half_life' => 'Half-Life',
|
||||||
'psa_stability' => 'Stability',
|
'psa_stability' => 'Stability',
|
||||||
'psa_solubility' => 'Solubility / Reconstitution',
|
'psa_solubility' => 'Solubility / Reconstitution',
|
||||||
'psa_vial_size_mg' => 'Common Vial Size (mg)',
|
'psa_vial_size_mg' => 'Common Vial Size (mg)',
|
||||||
'psa_storage_lyophilized' => 'Storage (Lyophilized)',
|
'psa_storage_lyophilized' => 'Storage (Lyophilized)',
|
||||||
'psa_storage_reconstituted' => 'Storage (Reconstituted)',
|
'psa_storage_reconstituted' => 'Storage (Reconstituted)',
|
||||||
'psa_typical_dose_mcg' => 'Typical Research Dose',
|
'psa_typical_dose_mcg' => 'Typical Research Dose',
|
||||||
'psa_cycle_parameters' => 'Cycle / Protocol Parameters',
|
'psa_cycle_parameters' => 'Cycle / Protocol Parameters',
|
||||||
'psa_amino_acid_count' => 'Amino Acid Count',
|
'psa_amino_acid_count' => 'Amino Acid Count',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,8 +121,11 @@ class PSA_Post_Type {
|
|||||||
if ( 'psa_source' === $column ) {
|
if ( 'psa_source' === $column ) {
|
||||||
$source = get_post_meta( $post_id, 'psa_source', true );
|
$source = get_post_meta( $post_id, 'psa_source', true );
|
||||||
$labels = array(
|
$labels = array(
|
||||||
'ai-generated' => 'AI', 'pubchem' => 'PubChem',
|
'ai-generated' => 'AI',
|
||||||
'manual' => 'Manual', 'pending' => 'Pending', 'failed' => 'Failed',
|
'pubchem' => 'PubChem',
|
||||||
|
'manual' => 'Manual',
|
||||||
|
'pending' => 'Pending',
|
||||||
|
'failed' => 'Failed',
|
||||||
);
|
);
|
||||||
echo esc_html( $labels[ $source ] ?? '—' );
|
echo esc_html( $labels[ $source ] ?? '—' );
|
||||||
}
|
}
|
||||||
@@ -140,14 +143,18 @@ class PSA_Post_Type {
|
|||||||
// ── Backward-compatible proxies (meta boxes moved to PSA_Post_Type_Meta) ──
|
// ── Backward-compatible proxies (meta boxes moved to PSA_Post_Type_Meta) ──
|
||||||
|
|
||||||
/** @see PSA_Post_Type_Meta::add_meta_boxes() */
|
/** @see PSA_Post_Type_Meta::add_meta_boxes() */
|
||||||
public static function add_meta_boxes(): void { PSA_Post_Type_Meta::add_meta_boxes(); }
|
public static function add_meta_boxes(): void {
|
||||||
|
PSA_Post_Type_Meta::add_meta_boxes(); }
|
||||||
|
|
||||||
/** @see PSA_Post_Type_Meta::render_meta_box() */
|
/** @see PSA_Post_Type_Meta::render_meta_box() */
|
||||||
public static function render_meta_box( $post ): void { PSA_Post_Type_Meta::render_meta_box( $post ); }
|
public static function render_meta_box( $post ): void {
|
||||||
|
PSA_Post_Type_Meta::render_meta_box( $post ); }
|
||||||
|
|
||||||
/** @see PSA_Post_Type_Meta::render_extended_meta_box() */
|
/** @see PSA_Post_Type_Meta::render_extended_meta_box() */
|
||||||
public static function render_extended_meta_box( $post ): void { PSA_Post_Type_Meta::render_extended_meta_box( $post ); }
|
public static function render_extended_meta_box( $post ): void {
|
||||||
|
PSA_Post_Type_Meta::render_extended_meta_box( $post ); }
|
||||||
|
|
||||||
/** @see PSA_Post_Type_Meta::save_meta() */
|
/** @see PSA_Post_Type_Meta::save_meta() */
|
||||||
public static function save_meta( int $post_id ): void { PSA_Post_Type_Meta::save_meta( $post_id ); }
|
public static function save_meta( int $post_id ): void {
|
||||||
|
PSA_Post_Type_Meta::save_meta( $post_id ); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,12 @@ class PSA_Search_Handler {
|
|||||||
$results = PSA_Search::search_peptides( $query );
|
$results = PSA_Search::search_peptides( $query );
|
||||||
|
|
||||||
if ( ! empty( $results['results'] ) ) {
|
if ( ! empty( $results['results'] ) ) {
|
||||||
wp_send_json_success( array( 'status' => 'found', 'data' => $results ) );
|
wp_send_json_success(
|
||||||
|
array(
|
||||||
|
'status' => 'found',
|
||||||
|
'data' => $results,
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +58,12 @@ class PSA_Search_Handler {
|
|||||||
|
|
||||||
// 3. Check rate limit before attempting new generation.
|
// 3. Check rate limit before attempting new generation.
|
||||||
if ( ! self::check_rate_limit() ) {
|
if ( ! self::check_rate_limit() ) {
|
||||||
wp_send_json_success( array( 'status' => 'rate_limited', 'message' => 'Too many requests. Please try again later.' ) );
|
wp_send_json_success(
|
||||||
|
array(
|
||||||
|
'status' => 'rate_limited',
|
||||||
|
'message' => 'Too many requests. Please try again later.',
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +88,12 @@ class PSA_Search_Handler {
|
|||||||
error_log( 'PSA: Max retries (' . PSA_Config::MAX_GENERATION_RETRIES . ') exceeded for "' . $pending->post_title . '" (post ' . $pending->ID . '). Marking as failed.' );
|
error_log( 'PSA: Max retries (' . PSA_Config::MAX_GENERATION_RETRIES . ') exceeded for "' . $pending->post_title . '" (post ' . $pending->ID . '). Marking as failed.' );
|
||||||
update_post_meta( $pending->ID, 'psa_source', 'failed' );
|
update_post_meta( $pending->ID, 'psa_source', 'failed' );
|
||||||
update_post_meta( $pending->ID, 'psa_generation_error', 'Generation failed after ' . PSA_Config::MAX_GENERATION_RETRIES . ' attempts.' );
|
update_post_meta( $pending->ID, 'psa_generation_error', 'Generation failed after ' . PSA_Config::MAX_GENERATION_RETRIES . ' attempts.' );
|
||||||
wp_send_json_success( array(
|
wp_send_json_success(
|
||||||
'status' => 'invalid',
|
array(
|
||||||
'message' => 'We were unable to generate content for this peptide. Please try again later.',
|
'status' => 'invalid',
|
||||||
) );
|
'message' => 'We were unable to generate content for this peptide. Please try again later.',
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +103,12 @@ class PSA_Search_Handler {
|
|||||||
self::schedule_background_generation( $pending->ID, $pending->post_title );
|
self::schedule_background_generation( $pending->ID, $pending->post_title );
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_send_json_success( array( 'status' => 'pending', 'peptide_name' => $pending->post_title ) );
|
wp_send_json_success(
|
||||||
|
array(
|
||||||
|
'status' => 'pending',
|
||||||
|
'peptide_name' => $pending->post_title,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,10 +141,12 @@ class PSA_Search_Handler {
|
|||||||
$daily_key = 'psa_daily_gen_' . gmdate( 'Y-m-d' );
|
$daily_key = 'psa_daily_gen_' . gmdate( 'Y-m-d' );
|
||||||
$daily_count = (int) get_transient( $daily_key );
|
$daily_count = (int) get_transient( $daily_key );
|
||||||
if ( $daily_count >= PSA_Config::DAILY_GENERATION_CAP ) {
|
if ( $daily_count >= PSA_Config::DAILY_GENERATION_CAP ) {
|
||||||
wp_send_json_success( array(
|
wp_send_json_success(
|
||||||
'status' => 'rate_limited',
|
array(
|
||||||
'message' => 'Our system has reached its daily limit for adding new peptides. Please try again tomorrow.',
|
'status' => 'rate_limited',
|
||||||
) );
|
'message' => 'Our system has reached its daily limit for adding new peptides. Please try again tomorrow.',
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,14 +154,21 @@ class PSA_Search_Handler {
|
|||||||
|
|
||||||
if ( is_wp_error( $validation ) ) {
|
if ( is_wp_error( $validation ) ) {
|
||||||
error_log( 'PSA: Validation error for "' . $query . '": ' . $validation->get_error_message() );
|
error_log( 'PSA: Validation error for "' . $query . '": ' . $validation->get_error_message() );
|
||||||
wp_send_json_success( array( 'status' => 'invalid', 'message' => 'Could not verify this peptide name.' ) );
|
wp_send_json_success(
|
||||||
|
array(
|
||||||
|
'status' => 'invalid',
|
||||||
|
'message' => 'Could not verify this peptide name.',
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( empty( $validation['is_valid'] ) ) {
|
if ( empty( $validation['is_valid'] ) ) {
|
||||||
wp_send_json_success( array(
|
wp_send_json_success(
|
||||||
'status' => 'invalid',
|
array(
|
||||||
'message' => $validation['reason'] ?? 'This does not appear to be a recognized peptide.',
|
'status' => 'invalid',
|
||||||
) );
|
'message' => $validation['reason'] ?? 'This does not appear to be a recognized peptide.',
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid peptide — increment rate limit and create placeholder.
|
// Valid peptide — increment rate limit and create placeholder.
|
||||||
@@ -165,7 +191,12 @@ class PSA_Search_Handler {
|
|||||||
error_log( 'PSA: Scheduling async generation for "' . $canonical_name . '" (post ' . $placeholder_id . ')' );
|
error_log( 'PSA: Scheduling async generation for "' . $canonical_name . '" (post ' . $placeholder_id . ')' );
|
||||||
self::schedule_background_generation( $placeholder_id, $canonical_name );
|
self::schedule_background_generation( $placeholder_id, $canonical_name );
|
||||||
|
|
||||||
wp_send_json_success( array( 'status' => 'pending', 'peptide_name' => $canonical_name ) );
|
wp_send_json_success(
|
||||||
|
array(
|
||||||
|
'status' => 'pending',
|
||||||
|
'peptide_name' => $canonical_name,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class PSA_Search {
|
|||||||
* @return string HTML output.
|
* @return string HTML output.
|
||||||
*/
|
*/
|
||||||
public static function render_search_form( $atts ): string {
|
public static function render_search_form( $atts ): string {
|
||||||
self::$instance_count++;
|
++self::$instance_count;
|
||||||
$instance = self::$instance_count;
|
$instance = self::$instance_count;
|
||||||
|
|
||||||
$atts = shortcode_atts(
|
$atts = shortcode_atts(
|
||||||
@@ -256,7 +256,11 @@ class PSA_Search {
|
|||||||
$results[] = self::format_peptide( (int) $id );
|
$results[] = self::format_peptide( (int) $id );
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = array( 'results' => $results, 'total' => count( $results ), 'query' => $query );
|
$response = array(
|
||||||
|
'results' => $results,
|
||||||
|
'total' => count( $results ),
|
||||||
|
'query' => $query,
|
||||||
|
);
|
||||||
|
|
||||||
set_transient( $cache_key, $response, 6 * HOUR_IN_SECONDS );
|
set_transient( $cache_key, $response, 6 * HOUR_IN_SECONDS );
|
||||||
|
|
||||||
@@ -290,11 +294,14 @@ class PSA_Search {
|
|||||||
// ── Backward-compatible proxies ─────────────────────────────────────
|
// ── Backward-compatible proxies ─────────────────────────────────────
|
||||||
|
|
||||||
/** @see PSA_Search_Handler::ajax_search() */
|
/** @see PSA_Search_Handler::ajax_search() */
|
||||||
public static function ajax_search(): void { PSA_Search_Handler::ajax_search(); }
|
public static function ajax_search(): void {
|
||||||
|
PSA_Search_Handler::ajax_search(); }
|
||||||
|
|
||||||
/** @see PSA_Search_Handler::create_pending_placeholder() */
|
/** @see PSA_Search_Handler::create_pending_placeholder() */
|
||||||
public static function create_pending_placeholder( string $peptide_name ) { return PSA_Search_Handler::create_pending_placeholder( $peptide_name ); }
|
public static function create_pending_placeholder( string $peptide_name ) {
|
||||||
|
return PSA_Search_Handler::create_pending_placeholder( $peptide_name ); }
|
||||||
|
|
||||||
/** @see PSA_Search_Handler::schedule_background_generation() */
|
/** @see PSA_Search_Handler::schedule_background_generation() */
|
||||||
public static function schedule_background_generation( int $post_id, string $peptide_name ): void { PSA_Search_Handler::schedule_background_generation( $post_id, $peptide_name ); }
|
public static function schedule_background_generation( int $post_id, string $peptide_name ): void {
|
||||||
|
PSA_Search_Handler::schedule_background_generation( $post_id, $peptide_name ); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ class PSA_Template {
|
|||||||
'class' => 'psa-badge-manual',
|
'class' => 'psa-badge-manual',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
$badge = $badges[ $source ] ?? $badges['ai-generated'];
|
$badge = $badges[ $source ] ?? $badges['ai-generated'];
|
||||||
|
|
||||||
$html = '<div class="psa-source-badge ' . esc_attr( $badge['class'] ) . '">';
|
$html = '<div class="psa-source-badge ' . esc_attr( $badge['class'] ) . '">';
|
||||||
$html .= esc_html( $badge['label'] );
|
$html .= esc_html( $badge['label'] );
|
||||||
|
|||||||
@@ -156,20 +156,20 @@ function psa_enqueue_frontend_assets() {
|
|||||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||||
'nonce' => wp_create_nonce( 'psa_search_nonce' ),
|
'nonce' => wp_create_nonce( 'psa_search_nonce' ),
|
||||||
'i18n' => array(
|
'i18n' => array(
|
||||||
'error' => __( 'Error:', 'peptide-search-ai' ),
|
'error' => __( 'Error:', 'peptide-search-ai' ),
|
||||||
'searchFailed' => __( 'Search failed.', 'peptide-search-ai' ),
|
'searchFailed' => __( 'Search failed.', 'peptide-search-ai' ),
|
||||||
'unexpected' => __( 'Unexpected response.', 'peptide-search-ai' ),
|
'unexpected' => __( 'Unexpected response.', 'peptide-search-ai' ),
|
||||||
'rateLimited' => __( 'Too many requests. Please try again later.', 'peptide-search-ai' ),
|
'rateLimited' => __( 'Too many requests. Please try again later.', 'peptide-search-ai' ),
|
||||||
'networkError' => __( 'Could not connect to the server. Please try again.', 'peptide-search-ai' ),
|
'networkError' => __( 'Could not connect to the server. Please try again.', 'peptide-search-ai' ),
|
||||||
'timeout' => __( 'Request timed out. The server took too long to respond. Please try again.', 'peptide-search-ai' ),
|
'timeout' => __( 'Request timed out. The server took too long to respond. Please try again.', 'peptide-search-ai' ),
|
||||||
'connectionError' => __( 'Network error. Please check your connection and try again.', 'peptide-search-ai' ),
|
'connectionError' => __( 'Network error. Please check your connection and try again.', 'peptide-search-ai' ),
|
||||||
'serverError' => __( 'Server error. Please try again later.', 'peptide-search-ai' ),
|
'serverError' => __( 'Server error. Please try again later.', 'peptide-search-ai' ),
|
||||||
'unavailable' => __( 'Server is temporarily unavailable. Please try again later.', 'peptide-search-ai' ),
|
'unavailable' => __( 'Server is temporarily unavailable. Please try again later.', 'peptide-search-ai' ),
|
||||||
'peptidesFound' => __( 'peptide(s) found', 'peptide-search-ai' ),
|
'peptidesFound' => __( 'peptide(s) found', 'peptide-search-ai' ),
|
||||||
'pendingMsg' => __( 'is currently being added to our database. Please check back again later.', 'peptide-search-ai' ),
|
'pendingMsg' => __( 'is currently being added to our database. Please check back again later.', 'peptide-search-ai' ),
|
||||||
'invalidMsg' => __( 'does not appear to be a recognized peptide. Please check the spelling or try a different search term.', 'peptide-search-ai' ),
|
'invalidMsg' => __( 'does not appear to be a recognized peptide. Please check the spelling or try a different search term.', 'peptide-search-ai' ),
|
||||||
'verified' => __( 'Verified', 'peptide-search-ai' ),
|
'verified' => __( 'Verified', 'peptide-search-ai' ),
|
||||||
'curated' => __( 'Curated', 'peptide-search-ai' ),
|
'curated' => __( 'Curated', 'peptide-search-ai' ),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -129,5 +129,7 @@ require_once ABSPATH . 'includes/class-psa-config.php';
|
|||||||
require_once ABSPATH . 'includes/class-psa-cost-tracker.php';
|
require_once ABSPATH . 'includes/class-psa-cost-tracker.php';
|
||||||
require_once ABSPATH . 'includes/class-psa-openrouter.php';
|
require_once ABSPATH . 'includes/class-psa-openrouter.php';
|
||||||
require_once ABSPATH . 'includes/class-psa-kb-builder.php';
|
require_once ABSPATH . 'includes/class-psa-kb-builder.php';
|
||||||
|
require_once ABSPATH . 'includes/class-psa-ai-content.php';
|
||||||
|
require_once ABSPATH . 'includes/class-psa-directory-api.php';
|
||||||
require_once ABSPATH . 'includes/class-psa-ai-generator.php';
|
require_once ABSPATH . 'includes/class-psa-ai-generator.php';
|
||||||
require_once ABSPATH . 'includes/class-psa-upgrade.php';
|
require_once ABSPATH . 'includes/class-psa-upgrade.php';
|
||||||
|
|||||||
Reference in New Issue
Block a user