log.php
273 lines
| 1 | <?php |
| 2 | |
| 3 | use IlluminateAgnostic\Arr\Support\Arr; |
| 4 | use Kubio\Core\Utils; |
| 5 | use Kubio\FileLog; |
| 6 | |
| 7 | function kubio_ai_get_logs() { |
| 8 | $logs_file = FileLog::get_log_files( 'AI' ); |
| 9 | $logs = array(); |
| 10 | $total_usage = 0; |
| 11 | $total_credits = 0; |
| 12 | $error_usage = 0; |
| 13 | $sections = 0; |
| 14 | $sections_credits = 0; |
| 15 | |
| 16 | $lines = array(); |
| 17 | |
| 18 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 19 | $tpc = Arr::get( $_REQUEST, 'tpc', 25 ); |
| 20 | $tpc = intval( $tpc ) ? intval( $tpc ) : 25; |
| 21 | |
| 22 | foreach ( $logs_file as $log_file ) { |
| 23 | $lines = array_merge( $lines, explode( "\n", file_get_contents( $log_file ) ) ); |
| 24 | } |
| 25 | |
| 26 | foreach ( $lines as $line ) { |
| 27 | $log = json_decode( $line, true ); |
| 28 | |
| 29 | if ( ! is_array( $log ) ) { |
| 30 | continue; |
| 31 | } |
| 32 | |
| 33 | $code = Arr::get( $log, 'data.code', 'Unknown code' ); |
| 34 | |
| 35 | $completion = Arr::get( $log, 'data.completion', array() ); |
| 36 | $completion = Utils::maybeJSONDecode( $completion ); |
| 37 | $type = Arr::get( $log, 'type', 'unknown' ); |
| 38 | $total_tokens = Arr::get( $log, 'data.usage.total_tokens', 0 ); |
| 39 | |
| 40 | $total_usage += $total_tokens; |
| 41 | $credits = $total_tokens / $tpc; |
| 42 | $credits = floor( $credits ) + 0.5 > $credits ? floor( $credits ) : ceil( $credits ); |
| 43 | |
| 44 | if ( $type === 'error' ) { |
| 45 | |
| 46 | $completion = is_array( $completion ) ? Utils::humanizeArray( $completion, ' ' ) : $completion; |
| 47 | $error_usage += $total_tokens; |
| 48 | } else { |
| 49 | $total_credits += $credits; |
| 50 | |
| 51 | if ( strpos( $code, '/generate-page-section' ) !== false || strpos( $code, '/rephrase-page-section' ) !== false ) { |
| 52 | ++$sections; |
| 53 | $sections_credits += $credits; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | $normalized_logs = array( |
| 58 | 'start_time' => Arr::get( $log, 'data.start_time', 0 ), |
| 59 | 'call_id' => Arr::get( $log, 'data.call_id', null ), |
| 60 | 'type' => $type, |
| 61 | 'ts' => Arr::get( $log, 'date', 0 ), |
| 62 | 'date' => gmdate( |
| 63 | 'Y-m-d H:i:s', |
| 64 | Arr::get( $log, 'date', 0 ) |
| 65 | ), |
| 66 | 'code' => $code, |
| 67 | 'prompts' => Arr::get( $log, 'data.prompts', array() ), |
| 68 | 'completion' => is_string( $completion ) ? $completion : json_encode( $completion, JSON_PRETTY_PRINT ), |
| 69 | 'usage' => array( |
| 70 | 'prompt' => Arr::get( $log, 'data.usage.prompt_tokens', 0 ), |
| 71 | 'completion' => Arr::get( $log, 'data.usage.completion_tokens', 0 ), |
| 72 | 'total' => $total_tokens, |
| 73 | 'credits' => $type === 'error' ? 0 : $credits, |
| 74 | ), |
| 75 | ); |
| 76 | |
| 77 | $logs[] = $normalized_logs; |
| 78 | |
| 79 | } |
| 80 | |
| 81 | usort( |
| 82 | $logs, |
| 83 | function ( $a, $b ) { |
| 84 | return $a['start_time'] - $b['start_time']; |
| 85 | } |
| 86 | ); |
| 87 | |
| 88 | $section_avg_credits = $sections ? round( $sections_credits / $sections, 1 ) : 0; |
| 89 | |
| 90 | return array( |
| 91 | 'logs' => $logs, |
| 92 | 'total_usage' => $total_usage, |
| 93 | 'error_usage' => $error_usage, |
| 94 | 'total_credits' => $total_credits, |
| 95 | 'tpc' => $tpc, |
| 96 | 'sections' => $sections, |
| 97 | 'sections_credits' => $sections_credits, |
| 98 | 'section_avg_credits' => $section_avg_credits, |
| 99 | ); |
| 100 | } |
| 101 | |
| 102 | ?> |
| 103 | <script> |
| 104 | let kubioAILogs = []; |
| 105 | function kubioAILogDownload(index){ |
| 106 | |
| 107 | const log = kubioAILogs[index]; |
| 108 | |
| 109 | const promptsArray = log.prompts.map(p=>{ |
| 110 | return [ |
| 111 | `### Role: ${p.role.toUpperCase()}`, |
| 112 | "\n", |
| 113 | "```", |
| 114 | p.content.replaceAll("```","``"), |
| 115 | "```", |
| 116 | ].join("\n") |
| 117 | }) |
| 118 | |
| 119 | const content = [ |
| 120 | `# Kubio AI Log`, |
| 121 | `Log type: ${log.type}`, |
| 122 | `Log code: ${log.code}`, |
| 123 | `\n`, |
| 124 | `Site: ${window.location}`, |
| 125 | `\n\n`, |
| 126 | `## PROMPTS`, |
| 127 | ...promptsArray, |
| 128 | `\n\n\n`, |
| 129 | `## COMPLETION`, |
| 130 | "```", |
| 131 | log.completion, |
| 132 | "```", |
| 133 | `\n\n\n`, |
| 134 | `## USAGE`, |
| 135 | `\n`, |
| 136 | "```", |
| 137 | JSON.stringify(log.usage || {},null,2), |
| 138 | "```", |
| 139 | ].join("\n"); |
| 140 | |
| 141 | |
| 142 | const file = new File([content], 'log.md', { |
| 143 | type: 'text/plain', |
| 144 | }); |
| 145 | |
| 146 | const link = document.createElement('a') |
| 147 | const url = URL.createObjectURL(file) |
| 148 | |
| 149 | link.href = url |
| 150 | link.download = file.name |
| 151 | document.body.appendChild(link) |
| 152 | link.click() |
| 153 | |
| 154 | document.body.removeChild(link) |
| 155 | window.URL.revokeObjectURL(url) |
| 156 | } |
| 157 | </script> |
| 158 | |
| 159 | <?php |
| 160 | // This option is available for developers only and is enabled by a wp-config flag |
| 161 | // as the content is generated by the server, we ignore the php code sniffer |
| 162 | // phpcs:disable |
| 163 | ?> |
| 164 | <div class="tab-page"> |
| 165 | <div class="kubio-logs-main-container"> |
| 166 | <div class="kubio-admin-page-section kubio-ai-logs-section"> |
| 167 | <div class="kubio-admin-page-section-content kubio-ai-logs"> |
| 168 | <div class="kubio-ai-section-title"> |
| 169 | <?php $kubio_ai_logs_data = kubio_ai_get_logs(); ?> |
| 170 | <h2> |
| 171 | Kubio AI Logs |
| 172 | <span> |
| 173 | <?php |
| 174 | printf( |
| 175 | 'Tokens: %1$s ( %2$s OK | %3$s ERR )', |
| 176 | $kubio_ai_logs_data['total_usage'], |
| 177 | $kubio_ai_logs_data['total_usage'] - $kubio_ai_logs_data['error_usage'], |
| 178 | $kubio_ai_logs_data['error_usage'] |
| 179 | ); |
| 180 | ?> |
| 181 | </span> |
| 182 | <span> |
| 183 | <?php |
| 184 | printf( |
| 185 | '%s credits ( %s for sections ) | %s sections | ~%s credits/section', |
| 186 | $kubio_ai_logs_data['total_credits'], |
| 187 | $kubio_ai_logs_data['sections_credits'], |
| 188 | $kubio_ai_logs_data['sections'], |
| 189 | $kubio_ai_logs_data['section_avg_credits'] |
| 190 | ); |
| 191 | ?> |
| 192 | </span> |
| 193 | |
| 194 | </h2> |
| 195 | <div class="align-items-end d-flex flex-column" style="gap:8px"> |
| 196 | <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'action', 'kubio_clear_ai_logs', admin_url( 'admin-ajax.php' ) ), 'kubio_clear_ai_logs' ) ); ?>" class="button-link-delete"><?php echo 'Clear logs'; ?></a> |
| 197 | <form method="get"> |
| 198 | <input name="page" value="kubio-get-started" type="hidden"/> |
| 199 | <input name="tab" value="ai-logs" type="hidden"/> |
| 200 | <label> |
| 201 | Tokens/Credit |
| 202 | <input name="tpc" value="<?php echo esc_attr( $kubio_ai_logs_data['tpc'] ); ?>"> |
| 203 | </label> |
| 204 | <button>Set</button> |
| 205 | </form> |
| 206 | </div> |
| 207 | </div> |
| 208 | <div class="kubio-ai-logs-wrapper"> |
| 209 | <?php foreach ( $kubio_ai_logs_data['logs'] as $log_index => $log ) : ?> |
| 210 | <details> |
| 211 | <script> |
| 212 | kubioAILogs.push(<?php echo json_encode( $log ); ?>); |
| 213 | </script> |
| 214 | <summary> |
| 215 | <span class="kubio-ai-log-type kubio-ai-log-type--<?php echo esc_attr( $log['type'] ); ?>"><?php echo $log['type']; ?></span> |
| 216 | <span class="kubio-ai-log-code"> |
| 217 | <?php echo $log['code']; ?> |
| 218 | </span> |
| 219 | <div class="kubio-ai-log-tags"> |
| 220 | <?php if ( Arr::get( $log, 'usage.total', 0 ) && $log['type'] !== 'error' ) : ?> |
| 221 | <span class="kubio-ai-log-tokens"><?php echo Arr::get( $log, 'usage.credits', 0 ); ?> credits ( <?php echo Arr::get( $log, 'usage.total', 0 ); ?> tokens )</span> |
| 222 | <?php endif; ?> |
| 223 | <?php printf( $log['call_id'] ? '<span class="kubio-ai-log-call-id">%s</span>' : '', $log['call_id'] ); ?> |
| 224 | <span class="kubio-ai-log-date"><?php echo $log['date']; ?></span> |
| 225 | <button onclick="kubioAILogDownload('<?php echo esc_attr( $log_index ); ?>')" class="button button-primary"> |
| 226 | <span class="dashicons dashicons-download " style="line-height: 30px;"></span> |
| 227 | </button> |
| 228 | </div> |
| 229 | </summary> |
| 230 | <div> |
| 231 | <div class="kubio-ai-log-prompts"> |
| 232 | <?php foreach ( $log['prompts'] as $prompt ) : ?> |
| 233 | <details class="kubio-ai-log-prompt"> |
| 234 | <summary class="kubio-ai-log-role"> |
| 235 | <span><?php echo Arr::get( $prompt, 'role', 'Unknown role' ); ?></span> |
| 236 | </summary> |
| 237 | <div class="kubio-ai-log-content"> |
| 238 | <?php |
| 239 | $prompt_content = Arr::get( $prompt, 'content', 'Unknown content' ); |
| 240 | |
| 241 | if ( ! is_string( $prompt_content ) && ! is_numeric( $prompt_content ) ) { |
| 242 | $prompt_content = json_encode( $prompt_content, JSON_PRETTY_PRINT ); |
| 243 | } |
| 244 | |
| 245 | printf( '<pre>%s</pre>', esc_html( $prompt_content ) ); |
| 246 | ?> |
| 247 | |
| 248 | </div> |
| 249 | </details> |
| 250 | <?php endforeach; ?> |
| 251 | </div> |
| 252 | <div class="kubio-ai-log-response"> |
| 253 | <pre><?php echo esc_html( $log['completion'] ); ?></pre> |
| 254 | </div> |
| 255 | <div class="kubio-ai-log-usage"> |
| 256 | <span class="kubio-ai-usage-prompt"><?php printf( 'Prompt tokens: %s', Arr::get( $log, 'usage.prompt', 0 ) ); ?></span> |
| 257 | <span class="kubio-ai-usage-completion"><?php printf( 'Completion tokens: %s', Arr::get( $log, 'usage.completion', 0 ) ); ?></span> |
| 258 | <span class="kubio-ai-usage-total"><?php printf( 'Total tokens: %s', Arr::get( $log, 'usage.total', 0 ) ); ?></span> |
| 259 | <span class="kubio-ai-usage-total"><?php printf( 'Credits: %s', Arr::get( $log, 'usage.credits', 0 ) ); ?></span> |
| 260 | </div> |
| 261 | </div> |
| 262 | </details> |
| 263 | <?php endforeach; ?> |
| 264 | </div> |
| 265 | </div> |
| 266 | |
| 267 | </div> |
| 268 | </div> |
| 269 | </div> |
| 270 | <?php |
| 271 | // phpcs:enable |
| 272 | ?> |
| 273 |