a
    'Ni2                     @   s8   d Z ddlmZ ddlmZmZ G dd dZe ZdS )z
API Usage Tracker for BaoLife

Tracks API usage and costs for OpenAI calls.
Provides insights into spending patterns and enables budget enforcement.
    )get_database_connection)datetime	timedeltac                   @   sr   e Zd ZdZdddddddZdd	 Zd
d ZdddZdddZdddZ	d ddZ
d!ddZd"ddZdS )#APIUsageTrackerzI
    Track API usage and costs for monitoring and budget management.
    gv!>gv!>)inputoutputgh㈵>gh㈵>)gpt-4o-minizgpt-4oc                 C   s
   d| _ dS )zInitialize API usage trackerFN)_table_created)self r   2/var/www/lichun.app/lichun/ws/api_usage_tracker.py__init__   s    zAPIUsageTracker.__init__c              
   C   s   t  }zzF| *}|d |  td W d   n1 sB0    Y  W n2 ty } ztd|  W Y d}~n
d}~0 0 W |  n
|  0 dS )z*Create api_usage table if it doesn't exista  
                    CREATE TABLE IF NOT EXISTS api_usage (
                        id INT AUTO_INCREMENT PRIMARY KEY,
                        player_id VARCHAR(36) NOT NULL,
                        conversation_id VARCHAR(36),
                        endpoint VARCHAR(50) NOT NULL,
                        model VARCHAR(50) NOT NULL,
                        prompt_tokens INT NOT NULL,
                        completion_tokens INT NOT NULL,
                        total_tokens INT NOT NULL,
                        cost_usd DECIMAL(10, 6) NOT NULL,
                        created_date DATETIME NOT NULL,
                        purpose VARCHAR(100),
                        INDEX idx_player (player_id),
                        INDEX idx_created (created_date),
                        INDEX idx_conversation (conversation_id),
                        INDEX idx_model (model)
                    )
                z API usage table created/verifiedNz"Failed to create api_usage table: )r   cursorexecutecommitprint	Exceptionclose)r
   mydbr   er   r   r   create_table_if_not_exists"   s    

*&z*APIUsageTracker.create_table_if_not_existsconversationc                 C   sD  | j s|   d| _ |dd}|dd}|dd}|| jv rh|| j| d  || j| d   }	n4|| jd d  || jd d   }	td	| d
 t }
zzP|
 4}|d|||||||	|f |
  W d   n1 s0    Y  W n4 t	y( } ztd|  W Y d}~n
d}~0 0 W |

  n
|

  0 |	S )a  
        Log API usage to database.

        Args:
            player_id: Player ID
            conversation_id: Conversation ID (optional)
            model: Model name (e.g., 'gpt-4o-mini')
            usage: Usage dict with 'prompt_tokens', 'completion_tokens', 'total_tokens'
            purpose: Purpose of the call (e.g., 'conversation', 'summarization', 'fact_extraction')
        Tprompt_tokensr   completion_tokenstotal_tokensr   r   r   zWarning: Unknown model 'z', using gpt-4o-mini pricingaJ  
                    INSERT INTO api_usage
                    (player_id, conversation_id, endpoint, model,
                     prompt_tokens, completion_tokens, total_tokens, cost_usd,
                     created_date, purpose)
                    VALUES (%s, %s, 'openai_chat', %s, %s, %s, %s, %s, NOW(), %s)
                NzFailed to track API usage: )r	   r   getPRICINGr   r   r   r   r   r   r   )r
   	player_idconversation_idmodelusagepurposer   r   r   costr   r   r   r   r   r   track_usageC   sF    

*&zAPIUsageTracker.track_usage   c              
   C   sN  t  }z6z|jdd}|d||f | }|r|d rt|d pJdt|d pXdt|d pfdt|d ptd|d	 |d
 |dW  d   W W |  S dddddd|dW  d   W W |  S W d   n1 s0    Y  W n@ ty2 } z&td|  W Y d}~W |  dS d}~0 0 W |  n
|  0 dS )z
        Get player's API usage statistics for last N days.

        Args:
            player_id: Player ID
            days: Number of days to look back

        Returns:
            Dict with usage statistics
        T
dictionarya  
                    SELECT
                        SUM(cost_usd) as total_cost,
                        SUM(total_tokens) as total_tokens,
                        COUNT(*) as total_calls,
                        AVG(cost_usd) as avg_cost_per_call,
                        MIN(created_date) as first_call,
                        MAX(created_date) as last_call
                    FROM api_usage
                    WHERE player_id = %s
                    AND created_date >= DATE_SUB(NOW(), INTERVAL %s DAY)
                total_calls
total_costr   r   avg_cost_per_call
first_call	last_call)r(   r   r'   r)   r*   r+   period_daysNzFailed to get player usage: )	r   r   r   fetchonefloatintr   r   r   )r
   r   daysr   r   resultr   r   r   r   get_player_usage   sD    $
z APIUsageTracker.get_player_usageNc              
   C   s   t  }zzv|jddV}|r.|d||f n|d|f | }dd |D W  d   W W |  S 1 sr0    Y  W n@ ty } z(td|  g W  Y d}~W |  S d}~0 0 W |  n
|  0 dS )	z
        Get usage breakdown by purpose.

        Args:
            player_id: Optional player ID (None for all players)
            days: Number of days to look back

        Returns:
            List of dicts with usage by purpose
        Tr%   a6  
                        SELECT
                            purpose,
                            COUNT(*) as call_count,
                            SUM(total_tokens) as total_tokens,
                            SUM(cost_usd) as total_cost,
                            AVG(cost_usd) as avg_cost
                        FROM api_usage
                        WHERE player_id = %s
                        AND created_date >= DATE_SUB(NOW(), INTERVAL %s DAY)
                        GROUP BY purpose
                        ORDER BY total_cost DESC
                    a  
                        SELECT
                            purpose,
                            COUNT(*) as call_count,
                            SUM(total_tokens) as total_tokens,
                            SUM(cost_usd) as total_cost,
                            AVG(cost_usd) as avg_cost
                        FROM api_usage
                        WHERE created_date >= DATE_SUB(NOW(), INTERVAL %s DAY)
                        GROUP BY purpose
                        ORDER BY total_cost DESC
                    c              	   S   s@   g | ]8}|d  t |d t |d t|d t|d dqS )r!   
call_countr   r(   avg_cost)r!   r3   r   r(   r4   )r/   r.   ).0rowr   r   r   
<listcomp>   s   



z8APIUsageTracker.get_usage_by_purpose.<locals>.<listcomp>Nz Failed to get usage by purpose: )r   r   r   fetchallr   r   r   )r
   r   r0   r   r   resultsr   r   r   r   get_usage_by_purpose   s,    z$APIUsageTracker.get_usage_by_purposec              
   C   s  t  }z z|jdd}|d|f | }|rt|d p@dt|d pNdt|d p\dt|d pjdt|d	 pxd|d
W  d   W W |  S W d   n1 s0    Y  W n@ t y } z&td|  W Y d}~W |  dS d}~0 0 W |  n
|  0 dS )z
        Get total usage across all players.

        Args:
            days: Number of days to look back

        Returns:
            Dict with aggregate statistics
        Tr%   a  
                    SELECT
                        COUNT(DISTINCT player_id) as unique_players,
                        SUM(cost_usd) as total_cost,
                        SUM(total_tokens) as total_tokens,
                        COUNT(*) as total_calls,
                        AVG(cost_usd) as avg_cost_per_call
                    FROM api_usage
                    WHERE created_date >= DATE_SUB(NOW(), INTERVAL %s DAY)
                unique_playersr   r(   r   r'   r)   )r;   r(   r   r'   r)   r,   NzFailed to get total usage: )	r   r   r   r-   r/   r.   r   r   r   )r
   r0   r   r   r1   r   r   r   r   get_total_usage   s.    
	$	zAPIUsageTracker.get_total_usage      @c                 C   sX   | j |dd}|rT|d }|| }|dkr6|| d nd}||||||k|dkdS dS )	z
        Check if player is within budget.

        Args:
            player_id: Player ID
            monthly_limit: Monthly budget limit in USD

        Returns:
            Dict with budget status
        r$   )r0   r(   r   d   P   )r(   monthly_limit	remainingpercentage_usedZover_budgetwarningN)r2   )r
   r   r@   r    r(   rA   rB   r   r   r   check_player_budget!  s    	z#APIUsageTracker.check_player_budget   c                 C   sD  t dd  |r6t d| d| d | ||}nt d| d | |}|r2t d  t d|d d	 t d
|d d t d|d d t d|d d	 |sd|v rt d|d   t dd  t d | ||}|D ]<}t d|d dd|d dd|d dd|d d qt d d dS ) z
        Print usage report to console.

        Args:
            player_id: Optional player ID (None for total usage)
            days: Number of days to report
        
z<============================================================zAPI Usage Report for Player z (Last z days)zTotal API Usage Report (Last zTotal Cost:      $r(   z.4fzTotal Tokens:    r   ,zTotal Calls:     r'   zAvg Cost/Call:   $r)   r;   zUnique Players:  z<------------------------------------------------------------zUsage by Purpose:z  r!   Z20sz
 | Calls: r3   Z4dz
 | Cost: $z6.4fz	 | Avg: $r4   N)r   r2   r<   r:   )r
   r   r0   r    Z
by_purposeitemr   r   r   print_usage_report>  s0    


z"APIUsageTracker.print_usage_report)r   )r$   )Nr$   )r$   )r=   )NrE   )__name__
__module____qualname____doc__r   r   r   r#   r2   r:   r<   rD   rI   r   r   r   r   r      s    !
@
9
:
+
r   N)rM   database.db_operationsr   r   r   r   trackerr   r   r   r   <module>   s     Y