refactor: extract PSA_Cost_Reporter from PSA_Cost_Tracker (337→171+136)
Monthly spend/token/estimate-count queries and recent-logs retrieval moved to PSA_Cost_Reporter. Tracker retains table management, logging, budget enforcement, and cost estimation. Shared month_range helper in reporter eliminates duplicated date-range logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
136
includes/class-psa-cost-reporter.php
Normal file
136
includes/class-psa-cost-reporter.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* Read-only reporting queries for API usage data.
|
||||
*
|
||||
* What: Monthly spend/token/estimate-count summaries and recent log retrieval.
|
||||
* Who calls it: PSA_Admin_Page (usage summary), PSA_Cost_Tracker (budget check).
|
||||
* Dependencies: WordPress $wpdb.
|
||||
*
|
||||
* @package PeptideSearchAI
|
||||
* @since 4.5.0
|
||||
* @see includes/class-psa-cost-tracker.php — Write-side: logging, cost estimation, table management.
|
||||
* @see includes/class-psa-admin-page.php — Renders usage summary using these queries.
|
||||
*/
|
||||
declare( strict_types=1 );
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class PSA_Cost_Reporter {
|
||||
|
||||
/**
|
||||
* Get total spend for a given month.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current).
|
||||
* @param int|null $month Month (defaults to current).
|
||||
* @return float Total USD spent.
|
||||
*/
|
||||
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float {
|
||||
global $wpdb;
|
||||
list( $start, $end ) = self::month_range( $year, $month );
|
||||
$table = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
'SELECT COALESCE(SUM(estimated_cost_usd), 0) FROM ' . $table . ' WHERE created_at >= %s AND created_at <= %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$start,
|
||||
$end
|
||||
)
|
||||
);
|
||||
|
||||
return floatval( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total token usage for a given month.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current).
|
||||
* @param int|null $month Month (defaults to current).
|
||||
* @return int Total tokens.
|
||||
*/
|
||||
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int {
|
||||
global $wpdb;
|
||||
list( $start, $end ) = self::month_range( $year, $month );
|
||||
$table = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
'SELECT COALESCE(SUM(total_tokens), 0) FROM ' . $table . ' WHERE created_at >= %s AND created_at <= %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$start,
|
||||
$end
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count API calls this month that used character-based token estimates.
|
||||
*
|
||||
* Why: Lets the admin UI flag when reported costs include approximations
|
||||
* so operators can distinguish measured spend from estimated spend.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current).
|
||||
* @param int|null $month Month (defaults to current).
|
||||
* @return int Number of estimated rows.
|
||||
*/
|
||||
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int {
|
||||
global $wpdb;
|
||||
list( $start, $end ) = self::month_range( $year, $month );
|
||||
$table = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
'SELECT COUNT(*) FROM ' . $table . ' WHERE created_at >= %s AND created_at <= %s AND token_source = %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$start,
|
||||
$end,
|
||||
'estimated'
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent API call logs.
|
||||
*
|
||||
* @param int $limit Maximum number of logs to return.
|
||||
* @return array Array of log row objects.
|
||||
*/
|
||||
public static function get_recent_logs( int $limit = 20 ): array {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'psa_api_logs';
|
||||
$limit = max( 1, (int) $limit );
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
'SELECT * FROM ' . $table . ' ORDER BY created_at DESC LIMIT %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$limit
|
||||
)
|
||||
);
|
||||
|
||||
return is_array( $results ) ? $results : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build start/end datetime strings for a given month.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current).
|
||||
* @param int|null $month Month (defaults to current).
|
||||
* @return array{0: string, 1: string} [start_date, end_datetime]
|
||||
*/
|
||||
private static function month_range( ?int $year, ?int $month ): array {
|
||||
$year = $year ?? (int) gmdate( 'Y' );
|
||||
$month = max( 1, min( 12, $month ?? (int) gmdate( 'm' ) ) );
|
||||
|
||||
$start = gmdate( 'Y-m-01', mktime( 0, 0, 0, $month, 1, $year ) );
|
||||
$end = gmdate( 'Y-m-t', mktime( 0, 0, 0, $month, 1, $year ) ) . ' 23:59:59';
|
||||
|
||||
return array( $start, $end );
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Tracks API usage costs and enforces monthly budget limits.
|
||||
* Logs API usage costs and enforces monthly budget limits.
|
||||
*
|
||||
* What: Logs every OpenRouter API call with token counts and estimated cost.
|
||||
* Provides budget enforcement and usage summaries for the admin UI.
|
||||
* Who calls it: PSA_AI_Generator (logs calls, checks budget), PSA_Admin (displays usage).
|
||||
* Dependencies: WordPress $wpdb, PSA_Config.
|
||||
* What: Table management, API call logging, cost estimation, budget enforcement.
|
||||
* Who calls it: PSA_OpenRouter (logs calls), PSA_AI_Generator (checks budget).
|
||||
* Dependencies: WordPress $wpdb, PSA_Config, PSA_Cost_Reporter (for spend queries).
|
||||
*
|
||||
* @package PeptideSearchAI
|
||||
* @since 1.0.0
|
||||
* @see includes/class-psa-cost-reporter.php — Read-side: monthly summaries, recent logs.
|
||||
*/
|
||||
declare( strict_types=1 );
|
||||
|
||||
@@ -17,17 +18,14 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
|
||||
class PSA_Cost_Tracker {
|
||||
|
||||
/**
|
||||
* Create the API logs table if it doesn't exist.
|
||||
* Uses dbDelta for safe schema updates.
|
||||
*/
|
||||
/** Create the API logs table if it doesn't exist. Uses dbDelta for safe schema updates. */
|
||||
public static function create_table(): void {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$table = $wpdb->prefix . 'psa_api_logs';
|
||||
$charset = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = "CREATE TABLE $table_name (
|
||||
$sql = "CREATE TABLE $table (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
provider VARCHAR(50) NOT NULL DEFAULT 'openrouter',
|
||||
@@ -42,13 +40,12 @@ class PSA_Cost_Tracker {
|
||||
token_source VARCHAR(20) NOT NULL DEFAULT 'api',
|
||||
INDEX idx_created_at (created_at),
|
||||
INDEX idx_request_type (request_type)
|
||||
) $charset_collate;";
|
||||
) $charset;";
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
dbDelta( $sql );
|
||||
|
||||
// Check for errors during table creation.
|
||||
if ( ! empty( $wpdb->last_error ) ) {
|
||||
error_log( 'PSA: Table creation error: ' . $wpdb->last_error );
|
||||
}
|
||||
@@ -58,63 +55,44 @@ class PSA_Cost_Tracker {
|
||||
* Log an API call with token counts and cost.
|
||||
*
|
||||
* @param array $data {
|
||||
* @type string $provider API provider (default: 'openrouter')
|
||||
* @type string $model Model identifier
|
||||
* @type int $prompt_tokens Tokens in prompt
|
||||
* @type int $completion_tokens Tokens in completion
|
||||
* @type int $total_tokens Total tokens (or 0 to auto-sum)
|
||||
* @type string $request_type 'validation' or 'generation'
|
||||
* @type string $peptide_name Name of peptide being processed
|
||||
* @type bool $success Whether API call succeeded
|
||||
* @type float $estimated_cost_usd Cost (or 0 to auto-calculate)
|
||||
* @type string $token_source 'api' (from usage response), 'estimated' (character-based), or 'none'
|
||||
* @type string $provider, @type string $model, @type int $prompt_tokens,
|
||||
* @type int $completion_tokens, @type int $total_tokens, @type string $request_type,
|
||||
* @type string $peptide_name, @type bool $success, @type float $estimated_cost_usd,
|
||||
* @type string $token_source 'api'|'estimated'|'none'
|
||||
* }
|
||||
* @return int|false Insert ID or false on error.
|
||||
*/
|
||||
public static function log_api_call( array $data ) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// Sanitize and prepare data.
|
||||
$provider = $data['provider'] ?? 'openrouter';
|
||||
$model = $data['model'] ?? '';
|
||||
$prompt_tokens = (int) ( $data['prompt_tokens'] ?? 0 );
|
||||
$table = $wpdb->prefix . 'psa_api_logs';
|
||||
$prompt_tokens = (int) ( $data['prompt_tokens'] ?? 0 );
|
||||
$completion_tokens = (int) ( $data['completion_tokens'] ?? 0 );
|
||||
$total_tokens = (int) ( $data['total_tokens'] ?? 0 );
|
||||
$request_type = $data['request_type'] ?? 'generation';
|
||||
$peptide_name = $data['peptide_name'] ?? '';
|
||||
$success = ! empty( $data['success'] ) ? 1 : 0;
|
||||
$estimated_cost = floatval( $data['estimated_cost_usd'] ?? 0 );
|
||||
$token_source = $data['token_source'] ?? 'api';
|
||||
$total_tokens = (int) ( $data['total_tokens'] ?? 0 );
|
||||
$estimated_cost = floatval( $data['estimated_cost_usd'] ?? 0 );
|
||||
|
||||
// Auto-sum tokens if not provided.
|
||||
if ( 0 === $total_tokens && ( $prompt_tokens > 0 || $completion_tokens > 0 ) ) {
|
||||
$total_tokens = $prompt_tokens + $completion_tokens;
|
||||
}
|
||||
|
||||
// Auto-calculate cost if not provided and we have token data.
|
||||
if ( 0 === $estimated_cost && $prompt_tokens > 0 ) {
|
||||
$estimated_cost = self::estimate_cost( $model, $prompt_tokens, $completion_tokens );
|
||||
$estimated_cost = self::estimate_cost( $data['model'] ?? '', $prompt_tokens, $completion_tokens );
|
||||
}
|
||||
|
||||
$insert_data = array(
|
||||
'provider' => sanitize_text_field( $provider ),
|
||||
'model' => sanitize_text_field( $model ),
|
||||
'provider' => sanitize_text_field( $data['provider'] ?? 'openrouter' ),
|
||||
'model' => sanitize_text_field( $data['model'] ?? '' ),
|
||||
'prompt_tokens' => $prompt_tokens,
|
||||
'completion_tokens' => $completion_tokens,
|
||||
'total_tokens' => $total_tokens,
|
||||
'estimated_cost_usd' => $estimated_cost,
|
||||
'request_type' => sanitize_text_field( $request_type ),
|
||||
'peptide_name' => sanitize_text_field( $peptide_name ),
|
||||
'success' => $success,
|
||||
'token_source' => sanitize_text_field( $token_source ),
|
||||
'request_type' => sanitize_text_field( $data['request_type'] ?? 'generation' ),
|
||||
'peptide_name' => sanitize_text_field( $data['peptide_name'] ?? '' ),
|
||||
'success' => ! empty( $data['success'] ) ? 1 : 0,
|
||||
'token_source' => sanitize_text_field( $data['token_source'] ?? 'api' ),
|
||||
);
|
||||
|
||||
$insert_format = array( '%s', '%s', '%d', '%d', '%d', '%f', '%s', '%s', '%d', '%s' );
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->insert( $table_name, $insert_data, $insert_format );
|
||||
$result = $wpdb->insert( $table, $insert_data, array( '%s', '%s', '%d', '%d', '%d', '%f', '%s', '%s', '%d', '%s' ) );
|
||||
|
||||
if ( false === $result ) {
|
||||
error_log( 'PSA: Failed to insert API log: ' . $wpdb->last_error );
|
||||
@@ -125,167 +103,23 @@ class PSA_Cost_Tracker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total spend for a given month.
|
||||
* Check if current month's budget has been exceeded. Budget of 0 = unlimited.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current year).
|
||||
* @param int|null $month Month (defaults to current month).
|
||||
* @return float Total USD spent in the month.
|
||||
*/
|
||||
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float {
|
||||
global $wpdb;
|
||||
|
||||
if ( null === $year ) {
|
||||
$year = (int) gmdate( 'Y' );
|
||||
}
|
||||
if ( null === $month ) {
|
||||
$month = (int) gmdate( 'm' );
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// Ensure valid month.
|
||||
$month = max( 1, min( 12, (int) $month ) );
|
||||
|
||||
// Build date range for the month.
|
||||
$start_date = gmdate( 'Y-m-01', mktime( 0, 0, 0, $month, 1, $year ) );
|
||||
$end_date = gmdate( 'Y-m-t', mktime( 0, 0, 0, $month, 1, $year ) );
|
||||
$end_date = $end_date . ' 23:59:59';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
'SELECT COALESCE(SUM(estimated_cost_usd), 0) FROM ' . $table_name . ' WHERE created_at >= %s AND created_at <= %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name is safe, built from $wpdb->prefix.
|
||||
$start_date,
|
||||
$end_date
|
||||
)
|
||||
);
|
||||
|
||||
return floatval( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total token usage for a given month.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current year).
|
||||
* @param int|null $month Month (defaults to current month).
|
||||
* @return int Total tokens used in the month.
|
||||
*/
|
||||
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int {
|
||||
global $wpdb;
|
||||
|
||||
if ( null === $year ) {
|
||||
$year = (int) gmdate( 'Y' );
|
||||
}
|
||||
if ( null === $month ) {
|
||||
$month = (int) gmdate( 'm' );
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// Ensure valid month.
|
||||
$month = max( 1, min( 12, (int) $month ) );
|
||||
|
||||
// Build date range for the month.
|
||||
$start_date = gmdate( 'Y-m-01', mktime( 0, 0, 0, $month, 1, $year ) );
|
||||
$end_date = gmdate( 'Y-m-t', mktime( 0, 0, 0, $month, 1, $year ) );
|
||||
$end_date = $end_date . ' 23:59:59';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
'SELECT COALESCE(SUM(total_tokens), 0) FROM ' . $table_name . ' WHERE created_at >= %s AND created_at <= %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name is safe, built from $wpdb->prefix.
|
||||
$start_date,
|
||||
$end_date
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count how many API calls this month used character-based token estimates.
|
||||
*
|
||||
* Why: Lets the admin UI flag when reported costs include approximations
|
||||
* so operators can distinguish measured spend from estimated spend.
|
||||
*
|
||||
* @param int|null $year Year (defaults to current year).
|
||||
* @param int|null $month Month (defaults to current month).
|
||||
* @return int Number of estimated rows this month.
|
||||
*/
|
||||
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int {
|
||||
global $wpdb;
|
||||
|
||||
if ( null === $year ) {
|
||||
$year = (int) gmdate( 'Y' );
|
||||
}
|
||||
if ( null === $month ) {
|
||||
$month = (int) gmdate( 'm' );
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
$month = max( 1, min( 12, (int) $month ) );
|
||||
|
||||
$start_date = gmdate( 'Y-m-01', mktime( 0, 0, 0, $month, 1, $year ) );
|
||||
$end_date = gmdate( 'Y-m-t', mktime( 0, 0, 0, $month, 1, $year ) ) . ' 23:59:59';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
'SELECT COUNT(*) FROM ' . $table_name . ' WHERE created_at >= %s AND created_at <= %s AND token_source = %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name is safe, built from $wpdb->prefix.
|
||||
$start_date,
|
||||
$end_date,
|
||||
'estimated'
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current month's budget has been exceeded.
|
||||
* Budget of 0 means unlimited.
|
||||
*
|
||||
* @return bool True if budget exceeded, false otherwise.
|
||||
* @return bool True if budget exceeded.
|
||||
*/
|
||||
public static function is_budget_exceeded(): bool {
|
||||
$settings = get_option( 'psa_settings', array() );
|
||||
$budget = floatval( $settings['monthly_budget'] ?? PSA_Config::DEFAULT_MONTHLY_BUDGET );
|
||||
|
||||
// Budget of 0 = unlimited. Use loose comparison because floatval() returns float.
|
||||
if ( 0.0 === $budget ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$current_spend = self::get_monthly_spend();
|
||||
return $current_spend >= $budget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent API call logs.
|
||||
*
|
||||
* @param int $limit Maximum number of logs to return.
|
||||
* @return array Array of log objects.
|
||||
*/
|
||||
public static function get_recent_logs( int $limit = 20 ): array {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
$limit = max( 1, (int) $limit );
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
'SELECT * FROM ' . $table_name . ' ORDER BY created_at DESC LIMIT %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name is safe, built from $wpdb->prefix.
|
||||
$limit
|
||||
)
|
||||
);
|
||||
|
||||
return is_array( $results ) ? $results : array();
|
||||
return PSA_Cost_Reporter::get_monthly_spend() >= $budget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate cost for an API call based on model and token counts.
|
||||
* Uses known pricing, defaults to conservative estimate for unknown models.
|
||||
*
|
||||
* @param string $model Model identifier.
|
||||
* @param int $prompt_tokens Tokens in prompt.
|
||||
@@ -293,45 +127,45 @@ class PSA_Cost_Tracker {
|
||||
* @return float Estimated cost in USD.
|
||||
*/
|
||||
public static function estimate_cost( string $model, int $prompt_tokens, int $completion_tokens ): float {
|
||||
// Known model pricing per 1 million tokens (input, output).
|
||||
// Why: OpenRouter pricing varies by model; we maintain known rates here and
|
||||
// fall back to a conservative default for unlisted models so cost tracking
|
||||
// always produces a usable upper-bound estimate.
|
||||
// Per 1M tokens (input, output). Conservative default for unknown models.
|
||||
$pricing = array(
|
||||
'google/gemini-2.5-flash' => array( 0.15, 0.60 ),
|
||||
'google/gemini-2.0-flash-001' => array( 0.10, 0.40 ),
|
||||
'deepseek/deepseek-v3.2' => array( 0.27, 1.10 ),
|
||||
'deepseek/deepseek-chat' => array( 0.27, 1.10 ),
|
||||
'deepseek/deepseek-r1' => array( 0.55, 2.19 ),
|
||||
'qwen/qwen3.6-plus:free' => array( 0.00, 0.00 ),
|
||||
'google/gemini-2.5-flash' => array( 0.15, 0.60 ),
|
||||
'google/gemini-2.0-flash-001' => array( 0.10, 0.40 ),
|
||||
'deepseek/deepseek-v3.2' => array( 0.27, 1.10 ),
|
||||
'deepseek/deepseek-chat' => array( 0.27, 1.10 ),
|
||||
'deepseek/deepseek-r1' => array( 0.55, 2.19 ),
|
||||
'qwen/qwen3.6-plus:free' => array( 0.00, 0.00 ),
|
||||
);
|
||||
|
||||
$model = sanitize_text_field( $model );
|
||||
|
||||
if ( isset( $pricing[ $model ] ) ) {
|
||||
list( $input_price, $output_price ) = $pricing[ $model ];
|
||||
list( $in, $out ) = $pricing[ $model ];
|
||||
} else {
|
||||
// Conservative default for unknown models.
|
||||
$input_price = 1.0;
|
||||
$output_price = 2.0;
|
||||
$in = 1.0;
|
||||
$out = 2.0;
|
||||
}
|
||||
|
||||
// Convert per 1M to per token.
|
||||
$input_cost = ( $input_price / 1000000 ) * $prompt_tokens;
|
||||
$output_cost = ( $output_price / 1000000 ) * $completion_tokens;
|
||||
|
||||
return (float) ( $input_cost + $output_cost );
|
||||
return (float) ( ( $in / 1000000 ) * $prompt_tokens + ( $out / 1000000 ) * $completion_tokens );
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the API logs table during uninstall.
|
||||
*/
|
||||
/** Drop the API logs table during uninstall. */
|
||||
public static function drop_table(): void {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'psa_api_logs';
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- table name is safe, built from $wpdb->prefix.
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . $table_name );
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'psa_api_logs' );
|
||||
}
|
||||
|
||||
// ── Backward-compatible proxies (reporting moved to PSA_Cost_Reporter) ──
|
||||
|
||||
/** @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 ); }
|
||||
|
||||
/** @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 ); }
|
||||
|
||||
/** @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 ); }
|
||||
|
||||
/** @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 ); }
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ require_once PSA_PLUGIN_DIR . 'includes/class-psa-post-type.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-post-type-meta.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-search-handler.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-search.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-cost-reporter.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-cost-tracker.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-openrouter.php';
|
||||
require_once PSA_PLUGIN_DIR . 'includes/class-psa-kb-builder.php';
|
||||
|
||||
Reference in New Issue
Block a user