a
    'Niù2  ã                   @   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ÚoutputgñhãˆµøÄ>gñhãˆµøä>)ú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 ƒ }z†zF| ¡ �*}| 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ƒ }
z’zP|
 ¡ �4}| d|||||||	|f¡ |
 ¡  W d  ƒ n1 sè0    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 r¤t|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 sæ0    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 ƒ }zÄzv|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¡ | ¡ }|rœt|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 s°0    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	›�ƒ |sÎd|v rÎt d|d › �ƒ t dd› �ƒ t dƒ |  ||¡}|D ]<}t d|d d›d|d d›d|d d›d|d d›�ƒ qôt 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