v0.3.0 — cost accountability + LLM call audit trail (#7)

Per-call recording of cost and I/O for every LLM probe. New tables prv_call_meta (schema v3) and prv_call_io. New admin pages: Costs (spend drill-down vs $5 cap) and Call Log (paginated + detail drawer). 90-day I/O retention prune via prv_daily_prune cron. P0 security: key never stored; gateway client exposes HTTP status only.

QA APPROVED 2026-06-15 SHA 09dc6db9ce
This commit is contained in:
peptiderepo
2026-06-15 17:07:08 +07:00
committed by GitHub
parent 2f4c1b2c72
commit 2e2c72cac6
37 changed files with 3246 additions and 325 deletions

View File

@@ -33,6 +33,13 @@ declare(strict_types=1);
*/
class PRV_Gateway_Client {
/**
* HTTP status code from the most recent request (set on permanent error).
*
* @var int
*/
private int $last_http_status = 200;
/**
* Base URL for the Cloudflare AI Gateway (without trailing slash).
*/
@@ -59,6 +66,9 @@ class PRV_Gateway_Client {
* @return array<string, mixed> Decoded JSON response body.
*
* @throws \RuntimeException On permanent HTTP failure or parse error.
* Exception message carries HTTP status code only;
* the response body is never included (P0: prevents
* secret-key leakage via 401 error responses).
*/
public function post_to_gateway( string $provider_slug, array $body, string $api_key, string $endpoint = '/chat/completions' ): array {
$base_url = $this->build_base_url( $provider_slug );
@@ -110,8 +120,9 @@ class PRV_Gateway_Client {
}
if ( $status >= 400 ) {
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- exception message, not HTML output
throw new \RuntimeException( sprintf( 'PR Vision gateway HTTP %d: %s', $status, substr( $raw_body, 0, 300 ) ) );
$this->last_http_status = $status;
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- exception message, not HTML output; body intentionally omitted (P0: prevents key-leak via 401 body)
throw new \RuntimeException( sprintf( 'PR Vision gateway HTTP %d', $status ) );
}
$data = json_decode( $raw_body, true );
@@ -129,6 +140,18 @@ class PRV_Gateway_Client {
throw new \RuntimeException( sprintf( 'PR Vision: gateway failed after %d attempts. Last: %s', PRV_MAX_RETRIES, $last_error ) );
}
/**
* Get the HTTP status code from the last failed request.
*
* Allows callers to record the HTTP status code in error records without
* parsing the exception message. Returns 200 when no failure has occurred.
*
* @return int HTTP status code.
*/
public function get_last_http_status(): int {
return $this->last_http_status;
}
/**
* Build the API base URL (gateway or direct fallback).
*