§
   äÔsp0ã                   ój  — d Z ddlZddlZddlZddlZddlZddlmZ ddlm	Z	m
Z
 dZdZdZdZd	Zdad
ZdZd„ Zd„ Zd„ Zd„ Ze
d„ ¦   «         Zd„ Zd„ Zd„ Z	 	 	 d3d„Zdefd„Zd4d„Zd„ Zd5d„Zd„ Z d6d„Z!d7d„Z"d5d „Z#d5d!„Z$d5d"„Z%d8d$„Z&d%„ Z'd&„ Z(d'„ Z)d4d(„Z*d4d)„Z+d*„ Z,d+„ Z-d,„ Z.d-„ Z/d.„ Z0d/„ Z1d0Z2d1„ Z3d2„ Z4dS )9am  SQLite store of the AI usage history: every call, its cost and the summaries the "Ceny AI" dialog shows.

The only module that runs SQL of the usage history. One connection per call (the plugin, the service and the game all
write), WAL journal, every write one transaction.

`calls` holds one row per AI call (a failed call too, with its error). Rows older than RETENTION_MONTHS are rolled up
into `daily` (one row per day, provider, model and purpose, kept for ever), so the totals stay right while the detail
shrinks. Every summary reads the view `usage`, which is both tables together; every delete removes from both.
é    N)Údefaultdict)ÚclosingÚcontextmanageré   zai_usage.dbzai_usage_stats.jsoné   ÚlegacyaI  
CREATE TABLE calls(
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  ts TEXT NOT NULL, day TEXT NOT NULL,
  provider TEXT NOT NULL, model TEXT NOT NULL, purpose TEXT NOT NULL,
  session_id TEXT NOT NULL DEFAULT '', title TEXT NOT NULL DEFAULT '',
  status TEXT NOT NULL DEFAULT 'ok', error TEXT NOT NULL DEFAULT '',
  latency_ms INTEGER NOT NULL DEFAULT 0, requests INTEGER NOT NULL DEFAULT 1,
  input_tokens INTEGER NOT NULL DEFAULT 0, cached_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0,
  input_cost REAL NOT NULL DEFAULT 0, cached_cost REAL NOT NULL DEFAULT 0, output_cost REAL NOT NULL DEFAULT 0,
  cost REAL NOT NULL DEFAULT 0, savings REAL NOT NULL DEFAULT 0);
CREATE INDEX calls_day ON calls(day);
CREATE INDEX calls_session ON calls(session_id);
CREATE TABLE daily(
  day TEXT NOT NULL, provider TEXT NOT NULL, model TEXT NOT NULL, purpose TEXT NOT NULL,
  requests INTEGER NOT NULL DEFAULT 0, errors INTEGER NOT NULL DEFAULT 0,
  input_tokens INTEGER NOT NULL DEFAULT 0, cached_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0,
  cost REAL NOT NULL DEFAULT 0, savings REAL NOT NULL DEFAULT 0,
  PRIMARY KEY(day, provider, model, purpose));
CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT);
CREATE VIEW usage AS
  SELECT day, provider, model, purpose, requests, CASE WHEN status = 'error' THEN requests ELSE 0 END AS errors,
         input_tokens, cached_tokens, output_tokens, cost, savings FROM calls
  UNION ALL
  SELECT day, provider, model, purpose, requests, errors, input_tokens, cached_tokens, output_tokens, cost, savings
  FROM daily;
zÉSUM(requests) AS requests, SUM(errors) AS errors, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens, SUM(cost) AS cost, SUM(savings) AS savingsc                 ó
   — | a dS )z*Tests point the store at a temporary file.N)Ú_override_path)Úpaths    ú`/Users/chudy/Library/Application Support/Kodi/addons/plugin.video.milionar/wcs/ai/usage_store.pyÚset_pathr   :   s   € ð €N€N€Nó    c                  ó4   — dd l } |                      d¦  «        S )Nr   z3special://profile/addon_data/plugin.video.milionar/)ÚxbmcvfsÚtranslatePath)r   s    r   Ú_profile_dirr   @   s    € Ø€N€N€NØ× Ò Ð!VÑWÔWÐWr   c                  ór   — t           p0t          j                             t	          ¦   «         t
          ¦  «        S ©N)r
   Úosr   Újoinr   ÚDB_NAME© r   r   Údb_pathr   E   s!   € ÝÐB�RœWŸ\š\­,©.¬.½'ÑBÔBÐBr   c                  óž   — t           j                             t           j                             t	          ¦   «         ¦  «        t
          ¦  «        S r   )r   r   r   Údirnamer   ÚLEGACY_JSONr   r   r   Úlegacy_json_pathr   I   s*   € ÝŒ7�<Š<�œŸš­©	¬	Ñ2Ô2µKÑ@Ô@Ð@r   c               #   óê  K  — t          ¦   «         } t          j        t          j                             | ¦  «        d¬¦  «         t          t          j        | d¬¦  «        ¦  «        5 }t          j        |_	        | 
                    d¦  «         t          |¦  «         	 |V — |                     ¦   «          n## t          $ r |                     ¦   «          ‚ w xY w	 ddd¦  «         dS # 1 swxY w Y   dS )zOA connection with the schema ready; commits on success, rolls back on an error.T)Úexist_oké   )ÚtimeoutzPRAGMA journal_mode=WALN)r   r   Úmakedirsr   r   r   Úsqlite3ÚconnectÚRowÚrow_factoryÚexecuteÚ_ensure_schemaÚcommitÚ	ExceptionÚrollback)r   Úcons     r   Ú_dbr-   M   s)  è è € õ ‰9Œ9€DÝ„K•”—’ Ñ%Ô%°Ð5Ñ5Ô5Ð5Ý	•” ¨qÐ1Ñ1Ô1Ñ	2Ô	2ð 	°cÝ!œ+ˆŒØ�ŠÐ-Ñ.Ô.Ð.Ý�sÑÔÐð	ØˆIˆIˆIØ�JŠJ‰LŒLˆLˆLøÝð 	ð 	ð 	Ø�LŠL‰NŒNˆNØð	øøøð ð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	s*   Á'6C(ÂB7Â6C(Â7 CÃC(Ã(C,Ã/C,c                 óJ  — |                       d¦  «                             ¦   «         d         t          k    rd S |                      t          ¦  «         t          | ¦  «         |                       d                     t          ¦  «        ¦  «         |                      ¦   «          d S )NzPRAGMA user_versionr   zPRAGMA user_version = {})r'   ÚfetchoneÚSCHEMA_VERSIONÚexecutescriptÚSCHEMAÚ_import_legacyÚformatr)   )r,   s    r   r(   r(   ^   s„   € Ø
‡{‚{Ð(Ñ)Ô)×2Ò2Ñ4Ô4°QÔ7½>ÒIÐIØˆØ×Ò•fÑÔÐÝ�3ÑÔÐØ‡K‚KÐ*×1Ò1µ.ÑAÔAÑBÔBÐBØ‡J‚J�L„L€L€L€Lr   c                  ó>   — t           j                             ¦   «         S r   )ÚdatetimeÚdateÚtodayr   r   r   Ú_todayr9   g   s   € ÝŒ=×ÒÑ Ô Ð r   c                 óö   — g g }}| r*|                      d¦  «         |                      | ¦  «         |r*|                      d¦  «         |                      |¦  «         |rdd                     |¦  «        z   nd|fS )zaWHERE clause and parameters for an inclusive range of days (YYYY-MM-DD); None leaves a side open.zday >= ?zday <= ?z WHERE z AND Ú )Úappendr   )ÚsinceÚuntilÚclausesÚparamss       r   Ú_rangesrA   k   s‰   € à˜"ˆV€GØð Ø�Š�zÑ"Ô"Ð"Ø�Š�eÑÔÐØð Ø�Š�zÑ"Ô"Ð"Ø�Š�eÑÔÐØ29ÐAˆI˜Ÿš WÑ-Ô-Ñ-Ð-¸rÀ6ÐIÐIr   r;   Úokç        c                 ó¨  — |pt           j                              ¦   «         }||z   |z   }t          ¦   «         5 }|                     d|                     ¦   «         |                     ¦   «                              ¦   «         | |||pd|pd||pddd…         t          |pd¦  «        ||	|
||||||f¦  «        }|j        cddd¦  «         S # 1 swxY w Y   dS )z#Stores one AI call; returns its id.zûINSERT INTO calls(ts, day, provider, model, purpose, session_id, title, status, error, latency_ms, requests, input_tokens, cached_tokens, output_tokens, input_cost, cached_cost, output_cost, cost, savings) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)r;   Né,  r   )r6   Únowr-   r'   Ú	isoformatr7   ÚintÚ	lastrowid)ÚproviderÚmodelÚpurposeÚ
session_idÚtitleÚstatusÚerrorÚ
latency_msÚrequestsÚinput_tokensÚcached_tokensÚoutput_tokensÚ
input_costÚcached_costÚoutput_costÚsavingsrF   Úcostr,   Úcursors                       r   Úrecord_callr\   x   s0  € ð Ð
(•Ô"×&Ò&Ñ(Ô(€CØ˜Ñ# kÑ1€DÝ	‰Œð  �#Ø—’ð<ð �]Š]‰_Œ_˜cŸhšh™jœj×2Ò2Ñ4Ô4°hÀÀwÐPZÐP`Ð^`ÐbgÐbmÐkmÐouØˆk�r˜4˜C˜4Ô ¥# j o°AÑ"6Ô"6¸À,ÐP]Ð_lØ˜ k°4¸ðBñ	Cô Cˆð Ôð ð  ð  ð  ñ  ô  ð  ð  ð  ð  ð  ð  øøøð  ð  ð  ð  ð  ð  s   ·BCÃCÃCc                 ój  — | pt          ¦   «         } | j        dz  | j        z   dz
  |z
  }t          j        |dz  |dz  dz   d¦  «                             ¦   «         }t          ¦   «         5 }|                     d|f¦  «         |                     d|f¦  «        j        cddd¦  «         S # 1 swxY w Y   dS )zmRolls the calls older than `months` months up into `daily` and removes them; returns how many were rolled up.r   r   aë  INSERT INTO daily(day, provider, model, purpose, requests, errors, input_tokens, cached_tokens, output_tokens, cost, savings) SELECT day, provider, model, purpose, SUM(requests), SUM(CASE WHEN status = 'error' THEN requests ELSE 0 END), SUM(input_tokens), SUM(cached_tokens), SUM(output_tokens), SUM(cost), SUM(savings) FROM calls WHERE day < ? GROUP BY day, provider, model, purpose ON CONFLICT(day, provider, model, purpose) DO UPDATE SET requests = requests + excluded.requests, errors = errors + excluded.errors, input_tokens = input_tokens + excluded.input_tokens, cached_tokens = cached_tokens + excluded.cached_tokens, output_tokens = output_tokens + excluded.output_tokens, cost = cost + excluded.cost, savings = savings + excluded.savingszDELETE FROM calls WHERE day < ?N)	r9   ÚyearÚmonthr6   r7   rG   r-   r'   Úrowcount)r8   ÚmonthsÚmonth_indexÚcutoffr,   s        r   Úprunerd   ‰   s  € àÐ•V‘X”X€EØ”*˜r‘/ E¤KÑ/°!Ñ3°fÑ<€KÝŒ]˜;¨"Ñ,¨k¸BÑ.>ÀÑ.BÀAÑFÔF×PÒPÑRÔR€FÝ	‰Œð 
R�#Ø�ŠðPð SYÐQZñ	\ô 	\ð 	\ð �{Š{Ð<¸v¸iÑHÔHÔQð
Rð 
Rð 
Rð 
Rñ 
Rô 
Rð 
Rð 
Rð 
Rð 
Rð 
Rð 
Røøøð 
Rð 
Rð 
Rð 
Rð 
Rð 
Rs   Á(3B(Â(B,Â/B,c                 óˆ  — | pt          ¦   «         } t          ¦   «         5 }|                     d¦  «                             ¦   «         }|r,|d         |                      ¦   «         k    r	 ddd¦  «         dS |                     d|                      ¦   «         f¦  «         ddd¦  «         n# 1 swxY w Y   t          | ¦  «        S )zprune() at most once a day.z+SELECT value FROM meta WHERE key = 'pruned'ÚvalueNr   z;INSERT OR REPLACE INTO meta(key, value) VALUES('pruned', ?))r9   r-   r'   r/   rG   rd   )r8   r,   Úrows      r   Úprune_if_duerh   ›   s"  € àÐ•V‘X”X€EÝ	‰Œð i�#Ø�kŠkÐGÑHÔH×QÒQÑSÔSˆØð 	�3�w”< 5§?¢?Ñ#4Ô#4Ò4Ð4Øðið ið iñ iô ið ið ið ið 	�ŠÐQÐTY×TcÒTcÑTeÔTeÐSgÑhÔhÐhð	ið ið iñ iô ið ið ið ið ið ið iøøøð ið ið ið iõ
 �‰<Œ<Ðs   ŸA	B*Á5)B*Â*B.Â1B.c                 óD   ‡ — ˆ fd„‰                       ¦   «         D ¦   «         S )Nc                 ó>   •— i | ]}||d vr
‰|         pdn‰|         “ŒS ))rJ   rK   rL   Údayr   r   ©Ú.0Úkeyrg   s     €r   ú
<dictcomp>z_row.<locals>.<dictcomp>¨   s9   ø€ Ð{Ð{Ð{ÐilˆC CÐ/VÐ$VÐ$V�#�c”(�-˜aøÐ\_Ð`cÔ\dÐ{Ð{Ð{r   ©Úkeys)rg   s   `r   Ú_rowrr   §   s*   ø€ Ø{Ð{Ð{Ð{Ðps×pxÒpxÑpzÔpzÐ{Ñ{Ô{Ð{r   c           	      ó  — t          | |¦  «        \  }}t          ¦   «         5 }t          |                     d                     t
          |¦  «        |¦  «                             ¦   «         ¦  «        cd d d ¦  «         S # 1 swxY w Y   d S )NzSELECT {} FROM usage{})rA   r-   rr   r'   r4   Ú_SUMSr/   )r=   r>   Úwherer@   r,   s        r   Útotalsrv   «   sÉ   € Ý˜E 5Ñ)Ô)�M€Eˆ6Ý	‰Œð c�#Ý�C—K’KÐ 8× ?Ò ?ÅÀuÑ MÔ MÈvÑVÔV×_Ò_ÑaÔaÑbÔbðcð cð cð cñ cô cð cð cð cð cð cð cøøøð cð cð cð cð cð cs   ¢AA=Á=BÂBc                 óv  — t          | |¦  «        \  }}t          ¦   «         5 }d„ |                     d                     t          |¦  «        |¦  «        D ¦   «         }ddd¦  «         n# 1 swxY w Y   t
          j                             | ¦  «        t
          j                             |¦  «        }}g }t          ||z
  j	        dz   ¦  «        D ]k}	|t          j
        |	¬¦  «        z                        ¦   «         }
|                     |                     |
¦  «        pt          |
ddddddd¬¦  «        ¦  «         Œl|S )	zVOne entry for every day of the range, empty days included (a chart wants all of them).c                 ó:   — i | ]}|d          t          |¦  «        “ŒS ©rk   ©rr   ©rm   rg   s     r   ro   zper_day.<locals>.<dictcomp>µ   s6   € ð Vð Vð V¨3��U”�T #™YœYð Vð Vð Vr   z(SELECT day, {} FROM usage{} GROUP BY dayNr   )Údaysr   rC   )rk   rR   ÚerrorsrS   rT   rU   rZ   rY   )rA   r-   r'   r4   rt   r6   r7   ÚfromisoformatÚranger|   Ú	timedeltarG   r<   ÚgetÚdict)r=   r>   ru   r@   r,   ÚfoundÚfirstÚlastr|   Úoffsetrk   s              r   Úper_dayr‡   ±   sŸ  € å˜E 5Ñ)Ô)�M€Eˆ6Ý	‰Œð V�#ðVð V°#·+²+Ø6×=Ò=½eÀUÑKÔKÈVñ3Uô 3Uð Vñ Vô VˆðVð Vð Vñ Vô Vð Vð Vð Vð Vð Vð Vøøøð Vð Vð Vð Võ ”-×-Ò-¨eÑ4Ô4µh´m×6QÒ6QÐRWÑ6XÔ6Xˆ4€EØ€DÝ˜ ™Ô+¨aÑ/Ñ0Ô0ð Tð TˆØ•xÔ)¨vÐ6Ñ6Ô6Ñ6×AÒAÑCÔCˆØ�Š�E—I’I˜c‘N”Nð S¥d¨s¸QÀqÐWXÐhiØ9:ÀÈcð'Sñ 'Sô 'Sñ 	Tô 	Tð 	Tð 	Tà€Ks   ¢:A(Á(A,Á/A,c                 óè   — t          ¦   «         5 }|                     d                     t          ¦  «        | f¦  «                             ¦   «         }d d d ¦  «         n# 1 swxY w Y   d„ |D ¦   «         S )Nz[SELECT substr(day, 1, 7) AS month, {} FROM usage GROUP BY month ORDER BY month DESC LIMIT ?c                 óP   ‡— g | ]"Šˆfd „‰                      ¦   «         D ¦   «         ‘Œ#S )c                 ó"   •— i | ]}|‰|         “ŒS r   r   rl   s     €r   ro   z(per_month.<locals>.<listcomp>.<dictcomp>Ä   ó   ø€ Ð1Ð1Ð1˜sˆS�#�c”(Ð1Ð1Ð1r   rp   r{   s    @r   ú
<listcomp>zper_month.<locals>.<listcomp>Ä   ó6   ø€ ÐBÐBÐB°cÐ1Ð1Ð1Ð1 c§h¢h¡j¤jÐ1Ñ1Ô1ÐBÐBÐBr   )r-   r'   r4   rt   Úfetchall)Úlimitr,   Úrowss      r   Ú	per_monthr‘   À   sµ   € Ý	‰Œð 0�#Ø�{Š{Ðx×Òõ  AFñ  Gô  GØ!˜8ñ%ô %ß%-¢X¡Z¤Zð 	ð0ð 0ð 0ñ 0ô 0ð 0ð 0ð 0ð 0ð 0ð 0øøøð 0ð 0ð 0ð 0ð CÐB¸TÐBÑBÔBÐBs   �AAÁA!Á$A!ú	cost DESCc           
      óð   — t          ||¦  «        \  }}t          ¦   «         5 }d„ |                     d                     | t          ||¬¦  «        |¦  «        D ¦   «         cd d d ¦  «         S # 1 swxY w Y   d S )Nc                 ó,   — g | ]}t          |¦  «        ‘ŒS r   rz   r{   s     r   rŒ   z_grouped.<locals>.<listcomp>Ê   s0   € ð Nð Nð N˜c•�S‘	”	ð Nð Nð Nr   zHSELECT {cols}, {sums} FROM usage{where} GROUP BY {cols} ORDER BY {order})ÚcolsÚsumsru   Úorder)rA   r-   r'   r4   rt   )Úcolumnsr=   r>   r—   ru   r@   r,   s          r   Ú_groupedr™   Ç   sê   € Ý˜E 5Ñ)Ô)�M€Eˆ6Ý	‰Œð N�#ðNð N S§[¢[ØV×]Ò]Ø¥5°¸Uð ^ñ Dô DØEKñ&Mô &Mð Nñ Nô NðNð Nð Nð Nñ Nô Nð Nð Nð Nð Nð Nð Nøøøð Nð Nð Nð Nð Nð Ns   ¢<A+Á+A/Á2A/c                 ó$   — t          d| |¦  «        S ©NrJ   ©r™   ©r=   r>   s     r   Úper_providerrž   Ï   s   € Ý�J  uÑ-Ô-Ð-r   c                 ó$   — t          d| |¦  «        S )Nzprovider, modelrœ   r�   s     r   Ú	per_modelr    Ó   s   € ÝÐ% u¨eÑ4Ô4Ð4r   c                 ó$   — t          d| |¦  «        S )NrL   rœ   r�   s     r   Úper_purposer¢   ×   s   € Ý�I˜u eÑ,Ô,Ð,r   rE   c                 ó
  — t          | |¦  «        \  }}t          ¦   «         5 }|                     d                     |¦  «        ||gz   ¦  «                             ¦   «         }ddd¦  «         n# 1 swxY w Y   d„ |D ¦   «         S )zNThe history: a game (all calls of one session) or a single call, newest first.a)  SELECT CASE WHEN session_id = '' THEN 'c:' || id ELSE 's:' || session_id END AS key, MAX(ts) AS last_ts, MIN(ts) AS first_ts, MAX(title) AS title, MAX(purpose) AS purpose, MAX(provider) AS provider, MAX(model) AS model, SUM(requests) AS requests, SUM(CASE WHEN status = 'error' THEN requests ELSE 0 END) AS errors, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens, SUM(cost) AS cost, SUM(savings) AS savings, SUM(latency_ms) AS latency_ms FROM calls{} GROUP BY key ORDER BY last_ts DESC LIMIT ?Nc                 óP   ‡— g | ]"Šˆfd „‰                      ¦   «         D ¦   «         ‘Œ#S )c                 ó"   •— i | ]}|‰|         “ŒS r   r   rl   s     €r   ro   z'sessions.<locals>.<listcomp>.<dictcomp>æ   r‹   r   rp   r{   s    @r   rŒ   zsessions.<locals>.<listcomp>æ   r�   r   )rA   r-   r'   r4   rŽ   )r=   r>   r�   ru   r@   r,   r�   s          r   Úsessionsr¦   Û   sá   € å˜E 5Ñ)Ô)�M€Eˆ6Ý	‰Œð r�#Ø�{Š{ðF÷
 GMÂfÈUÁmÄmÐU[Ð_dÐ^eÑUeñgô g÷ hpÒgoÑgqÔgqð 	ðrð rð rñ rô rð rð rð rð rð rð røøøð rð rð rð rð CÐB¸TÐBÑBÔBÐBs   ¢A A.Á.A2Á5A2c           	      ó,  — t          ¦   «         5 }|                      d¦  «        r |                     d| dd…         f¦  «        }n,|                     dt          | dd…         ¦  «        f¦  «        }d„ |D ¦   «         cddd¦  «         S # 1 swxY w Y   dS )z>The calls of a history entry (`sessions()` key), oldest first.ús:z4SELECT * FROM calls WHERE session_id = ? ORDER BY tsé   Nz SELECT * FROM calls WHERE id = ?c                 óP   ‡— g | ]"Šˆfd „‰                      ¦   «         D ¦   «         ‘Œ#S )c                 ó"   •— i | ]}|‰|         “ŒS r   r   )rm   Úkrg   s     €r   ro   z'calls_of.<locals>.<listcomp>.<dictcomp>ð   s   ø€ Ð/Ð/Ð/˜q��C˜”FÐ/Ð/Ð/r   rp   r{   s    @r   rŒ   zcalls_of.<locals>.<listcomp>ð   s6   ø€ Ð@Ð@Ð@°CÐ/Ð/Ð/Ð/ C§H¢H¡J¤JÐ/Ñ/Ô/Ð@Ð@Ð@r   )r-   Ú
startswithr'   rH   )rn   r,   r�   s      r   Úcalls_ofr®   é   sð   € å	‰Œð A�#Ø�>Š>˜$ÑÔð 	TØ—;’;ÐUÐX[Ð\]Ð\^Ð\^ÔX_ÐWaÑbÔbˆDˆDà—;’;ÐAÅCÈÈAÈBÈBÌÁLÄLÀ?ÑSÔSˆDØ@Ð@¸4Ð@Ñ@Ô@ðAð Að Að Añ Aô Að Að Að Að Að Að Aøøøð Að Að Að Að Að As   �A-B	Â	BÂBc                  óÐ   — t          ¦   «         5 } |                      d¦  «                             ¦   «         }ddd¦  «         n# 1 swxY w Y   |d         r|d         |d         fndS )z.(first day, last day) with any usage, or None.z5SELECT MIN(day) AS first, MAX(day) AS last FROM usageNr„   r…   )r-   r'   r/   )r,   rg   s     r   Úspanr°   ó   s­   € å	‰Œð ^�#Ø�kŠkÐQÑRÔR×[Ò[Ñ]Ô]ˆð^ð ^ð ^ñ ^ô ^ð ^ð ^ð ^ð ^ð ^ð ^øøøð ^ð ^ð ^ð ^à*-¨g¬,Ð@ˆC�ŒL˜#˜fœ+Ð&Ð&¸DÐ@s   �(AÁAÁ
Ac                 óX  — | dk    r>|                      d¦  «        rdd|dd…         ffS ddt          |dd…         ¦  «        ffS | dk    rddt          |¦  «        ffS | dk    rd	d	|ffS | d
k    rdd|ffS | dk    rdd|ffS | dk    rdS t          d                     | ¦  «        ¦  «        ‚)zY(WHERE for `calls`, WHERE for `daily` or None when only `calls` can hold it, parameters).Úentryr¨   zsession_id = ?Nr©   zid = ?Úcallrk   zday = ?r_   zsubstr(day, 1, 7) = ?rJ   zprovider = ?Úall)ú1 = 1rµ   r   zunknown delete scope: {})r­   rH   Ú
ValueErrorr4   )Úkindrf   s     r   Ú_scoper¸   û   së   € àˆw‚€Ø×Ò˜DÑ!Ô!ð 	8Ø# T¨E°!°"°"¬I¨<Ð7Ð7Ø˜¥ E¨!¨"¨"¤I¡¤Ð0Ð0Ð0Øˆv‚~€~Ø˜¥ E¡
¤
˜}Ð,Ð,Øˆu‚}€}Ø˜) e XÐ-Ð-Øˆw‚€Ø&Ð(?À%ÀÐIÐIØˆzÒÐØ˜~°¨xÐ7Ð7Øˆu‚}€}Ø#Ð#Ý
Ð/×6Ò6°tÑ<Ô<Ñ
=Ô
=Ð=r   c                 óè  — t          | |¦  «        \  }}}t          ¦   «         5 }|                     d                     |¦  «        |¦  «                             ¦   «         }|r;|                     d                     |¦  «        |¦  «                             ¦   «         nd}ddd¦  «         n# 1 swxY w Y   |d         |r|d         ndz   |d         |r|d         ndz   |d         |r|d         ndz   d	œS )
zoWhat deleting a scope would remove: {'records': rows (calls + rolled-up days), 'requests': calls, 'cost': USD}.zpSELECT COUNT(*) AS n, COALESCE(SUM(requests), 0) AS requests, COALESCE(SUM(cost), 0) AS cost FROM calls WHERE {}zpSELECT COUNT(*) AS n, COALESCE(SUM(requests), 0) AS requests, COALESCE(SUM(cost), 0) AS cost FROM daily WHERE {}NÚnr   rR   rZ   rC   )ÚrecordsrR   rZ   )r¸   r-   r'   r4   r/   )r·   rf   Úcalls_whereÚdaily_wherer@   r,   Úcallsr|   s           r   Úpreview_deleter¿     sq  € å'-¨d°EÑ':Ô':Ñ$€K�˜fÝ	‰Œð r�#Ø—’ð 2ß28²&¸Ñ2EÔ2EÀvñOô OßOWÊxÉzÌzð 	ð ]hðrˆs�{Š{ð 1ß17²¸Ñ1DÔ1DÀfñNô NßNVÊhÉjÌjÈjÐmqð 	ðrð rð rñ rô rð rð rð rð rð rð røøøð rð rð rð rð
 ˜S”z°$Ð%= T¨#¤Y Y¸AÑ>ÈEÐR\ÔL]Ðuyð  bAÐaeÐfpÔaqÐaqð  @Añ  MBØ˜&”M°TÐ%B T¨&¤\ \¸sÑCðEð Eð Es   £A;B*Â*B.Â1B.c                 ó@  — t          | |¦  «        \  }}}t          ¦   «         5 }|                     d                     |¦  «        |¦  «        j        }|r1||                     d                     |¦  «        |¦  «        j        z  }|cddd¦  «         S # 1 swxY w Y   dS )zERemoves a scope from both tables; returns how many rows were removed.zDELETE FROM calls WHERE {}zDELETE FROM daily WHERE {}N)r¸   r-   r'   r4   r`   )r·   rf   r¼   r½   r@   r,   Úremoveds          r   ÚdeleterÂ     sà   € å'-¨d°EÑ':Ô':Ñ$€K�˜fÝ	‰Œð �#Ø—+’+Ð:×AÒAÀ+ÑNÔNÐPVÑWÔWÔ`ˆØð 	fØ�s—{’{Ð#?×#FÒ#FÀ{Ñ#SÔ#SÐU[Ñ\Ô\ÔeÑeˆGØð	ð ð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð ð s   £A#BÂBÂBc                 ó"   — t          d| ¦  «        S )Nr²   ©rÂ   )rn   s    r   Údelete_entryrÅ   $  s   € Ý�'˜3ÑÔÐr   c                 ó"   — t          d| ¦  «        S )Nr³   rÄ   )Úcall_ids    r   Údelete_callrÈ   (  s   € Ý�&˜'Ñ"Ô"Ð"r   c                 ó"   — t          d| ¦  «        S )Nrk   rÄ   ry   s    r   Ú
delete_dayrÊ   ,  s   € Ý�%˜ÑÔÐr   c                 ó"   — t          d| ¦  «        S )Nr_   rÄ   )r_   s    r   Údelete_monthrÌ   0  s   € Ý�'˜5Ñ!Ô!Ð!r   c                 ó"   — t          d| ¦  «        S r›   rÄ   )rJ   s    r   Údelete_providerrÎ   4  s   € Ý�*˜hÑ'Ô'Ð'r   c                  ó    — t          d¦  «        S )Nr´   rÄ   r   r   r   Ú
delete_allrÐ   8  s   € Ý�%‰=Œ=Ðr   )ÚtsrJ   rK   rL   rN   rO   rP   rQ   rR   rS   rT   rU   rZ   rY   c                 óÄ  ‡— t          ¦   «         5 }|                     d                     d                     t          ¦  «        ¦  «        ¦  «                             ¦   «         }|                     d¦  «                             ¦   «         }ddd¦  «         n# 1 swxY w Y   t          | ddd¬¦  «        5 }t          j        |¦  «        }| 	                    t          ¦  «         t          |¦  «        t          |¦  «        z   D ](Š| 	                    ˆfd	„t          D ¦   «         ¦  «         Œ)	 ddd¦  «         n# 1 swxY w Y   t          |¦  «        t          |¦  «        z   S )
zNWrites every call (and the rolled-up days) as CSV; returns the number of rows.z SELECT {} FROM calls ORDER BY tsz, zÇSELECT day AS ts, provider, model, purpose, '' AS title, 'summary' AS status, '' AS error, 0 AS latency_ms, requests, input_tokens, cached_tokens, output_tokens, cost, savings FROM daily ORDER BY dayNÚwúutf-8r;   )ÚencodingÚnewlinec                 ó‚   •— g | ];}t          ‰|         t          ¦  «        rt          ‰|         d ¦  «        n‰|         ‘Œ<S )é   )Ú
isinstanceÚfloatÚround)rm   Úcolumnrg   s     €r   rŒ   zexport_csv.<locals>.<listcomp>L  sY   ø€ ð  Dð  Dð  DÐjpµjÀÀVÄÍeÑ6TÔ6TÐe�U 3 v¤;°Ñ2Ô2Ð2ÐZ]Ð^dÔZeð  Dð  Dð  Dr   )r-   r'   r4   r   ÚEXPORT_COLUMNSrŽ   ÚopenÚcsvÚwriterÚwriterowÚlistÚlen)r   r,   r¾   ÚdailyÚhandlerà   rg   s         @r   Ú
export_csvræ   A  sÿ  ø€ å	‰Œð v�#Ø—’Ð>×EÒEÀdÇiÂiÕP^ÑF_ÔF_Ñ`Ô`ÑaÔa×jÒjÑlÔlˆØ—’ðjñkô kçksÒksÑkuÔkuð 	ðvð vð vñ vô vð vð vð vð vð vð vøøøð vð vð vð võ
 
ˆd�C '°2Ð	6Ñ	6Ô	6ð E¸&Ý”˜FÑ#Ô#ˆØ�Š�Ñ'Ô'Ð'Ý˜‘;”;¥ e¡¤Ñ,ð 	Eð 	EˆCØ�OŠOð  Dð  Dð  Dð  Dõ  uCð  Dñ  Dô  Dñ  Eô  Eð  Eð  Eð	EðEð Eð Eñ Eô Eð Eð Eð Eð Eð Eð Eøøøð Eð Eð Eð Eõ
 ˆu‰:Œ:�˜E™
œ
Ñ"Ð"s%   �A:BÂBÂBÂ4A7D8Ä8D<Ä?D<c                 óÀ  — t          ¦   «         }t          j                             |¦  «        sd S 	 t	          |d¬¦  «        5 }t          j        |¦  «        }d d d ¦  «         n# 1 swxY w Y   n# t          t          f$ r Y d S w xY wt          d„ ¦  «        }| 
                    dg ¦  «        D �]¨}| 
                    d¦  «        pd}| 
                    dd¦  «        | 
                    dd¦  «        }}|r|r|sŒMt          d	t          | 
                    d
¦  «        pd	¦  «        ¦  «        }	||d d…         ||| 
                    d¦  «        pd| 
                    d¦  «        pd|	| 
                    dd¦  «        | 
                    dd¦  «        | 
                    dd¦  «        | 
                    dd¦  «        f}
|                      d|
¦  «         |||f         }t          |	| 
                    dd¦  «        | 
                    dd¦  «        | 
                    dd¦  «        | 
                    dd¦  «        f¦  «        D ]\  }}||xx         |z  cc<   Œ�Œª| 
                    d¦  «        pdd d…         pt          ¦   «                              ¦   «         }| 
                    di ¦  «                             ¦   «         D �]t\  }}| 
                    di ¦  «                             ¦   «         D �]D\  }}|||f         }t          d| 
                    dd¦  «        |d         z
  ¦  «        t          d| 
                    dd¦  «        |d	         z
  ¦  «        t          d| 
                    dd¦  «        |d         z
  ¦  «        t          d| 
                    dd¦  «        |d         z
  ¦  «        t          d| 
                    dd¦  «        |d         z
  ¦  «        g}|d         s|d	         dk    rB|                      d|||t$          |d         |d         |d         |d         |d	         f	¦  «         �ŒF�Œv	 t          j        ||d z   ¦  «         d S # t          $ r Y d S w xY w)!NrÔ   )rÕ   c                  ó
   — g d¢S )N)r   rC   r   r   r   r   r   r   r   ú<lambda>z _import_legacy.<locals>.<lambda>Z  s   € Ð#4Ð#4Ð#4€ r   Úcall_historyÚ	timestampr;   rJ   rK   r   Úinference_counté
   Úrequest_typeÚotherrM   rS   r   rT   rU   rZ   rC   zšINSERT INTO calls(ts, day, provider, model, purpose, session_id, requests, input_tokens, cached_tokens, output_tokens, cost) VALUES(?,?,?,?,?,?,?,?,?,?,?)ÚcreatedÚ	providersÚmodelsrR   Ú
total_costr©   é   é   g•Ö&è.>z†INSERT INTO daily(day, provider, model, purpose, requests, input_tokens, cached_tokens, output_tokens, cost) VALUES(?,?,?,?,?,?,?,?,?)z	.migrated)r   r   r   ÚexistsrÞ   ÚjsonÚloadÚOSErrorr¶   r   r�   ÚmaxrH   r'   Ú	enumerater9   rG   ÚitemsÚLEGACY_PURPOSEÚreplace)r,   r   rå   ÚdataÚimportedÚrecordÚstamprJ   rK   rR   ÚvaluesÚtotalÚindexrf   rk   Úprovider_dataÚ
model_dataÚdoneÚrests                      r   r3   r3   Q  så  € ÝÑÔ€DÝŒ7�>Š>˜$ÑÔð ØˆðÝ�$ Ð)Ñ)Ô)ð 	%¨VÝ”9˜VÑ$Ô$ˆDð	%ð 	%ð 	%ñ 	%ô 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%øøøð 	%ð 	%ð 	%ð 	%øøå•ZÐ ð ð ð ØˆˆðøøøåÐ4Ð4Ñ5Ô5€HØ—(’(˜>¨2Ñ.Ô.ð "ñ "ˆØ—
’
˜;Ñ'Ô'Ð-¨2ˆØ Ÿ*š* Z°Ñ4Ô4°f·j²jÀÈ"Ñ6MÔ6M�%ˆØð 	˜(ð 	 uð 	ØÝ�q�#˜fŸjšjÐ):Ñ;Ô;Ð@¸qÑAÔAÑBÔBˆØ˜˜s ˜sœ X¨u°f·j²jÀÑ6PÔ6PÐ6[ÐT[Ð]c×]gÒ]gÐhtÑ]uÔ]uÐ]{Ðy{Ø˜FŸJšJ ~°qÑ9Ô9¸6¿:º:ÀoÐWXÑ;YÔ;YÐ[a×[eÒ[eÐfuÐwxÑ[yÔ[yØ—*’*˜V SÑ)Ô)ð+ˆð 	�Šð IØJPñ	Rô 	Rð 	Rà˜( EÐ*Ô+ˆÝ% x°·²¸FÀCÑ1HÔ1HÈ&Ï*Ê*ÐUcÐefÑJgÔJgØ'-§z¢z°/À1Ñ'EÔ'EÀvÇzÂzÐRaÐcdÑGeÔGeð'gñ hô hð 	"ð 	"‰LˆE�5à�%ˆLˆLŒL˜EÑ!ˆLˆL‰LˆLñ	"ð �8Š8�IÑÔÐ$ " c r cÔ
*Ð
B­f©h¬h×.@Ò.@Ñ.BÔ.B€CØ#'§8¢8¨K¸Ñ#<Ô#<×#BÒ#BÑ#DÔ#Dð 	qñ 	qÑˆ�-Ø!.×!2Ò!2°8¸RÑ!@Ô!@×!FÒ!FÑ!HÔ!Hð 	qñ 	qÑˆE�:Ø˜X uÐ-Ô.ˆDÝ˜˜:Ÿ>š>¨*°aÑ8Ô8¸4À¼7ÑBÑCÔCÅSÈÈjÏnÊnÐ]iÐknÑNoÔNoÐrvÐwxÔryÑNyÑEzÔEzÝ˜˜:Ÿ>š>¨.¸!Ñ<Ô<¸tÀA¼wÑFÑGÔGÍÈQÐPZ×P^ÒP^Ð_nÐpqÑPrÔPrÐuyÐz{Ôu|ÑP|ÑI}ÔI}Ý˜˜:Ÿ>š>¨/¸1Ñ=Ô=ÀÀQÄÑGÑHÔHðJˆDð �AŒwð q˜$˜qœ' Dš.˜.Ø—’ð Mà  (¨Eµ>À4ÈÄ7ÈDÐQRÌGÐUYÐZ[ÔU\Ð^bÐcdÔ^eÐgkÐlmÔgnÐoñqô qð qùñ	qðÝ
Œ
�4˜ Ñ+Ñ,Ô,Ð,Ð,Ð,øÝð ð ð ØˆˆðøøøsG   ±A/ ÁA#ÁA/ Á#A'Á'A/ Á*A'Á+A/ Á/BÂBÐ5Q Ñ
QÑQ)r;   r;   rB   r;   r   r   r   r   r   rC   rC   rC   rC   Nr   )NN)r   )r’   )NNrE   )5Ú__doc__rß   r6   r÷   r   r#   Úcollectionsr   Ú
contextlibr   r   r0   r   r   ÚRETENTION_MONTHSrý   r
   r2   rt   r   r   r   r   r-   r(   r9   rA   r\   rd   rh   rr   rv   r‡   r‘   r™   rž   r    r¢   r¦   r®   r°   r¸   r¿   rÂ   rÅ   rÈ   rÊ   rÌ   rÎ   rÐ   rÝ   ræ   r3   r   r   r   ú<module>r     s!  ððð ð €
€
€
Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø #Ð #Ð #Ð #Ð #Ð #Ø .Ð .Ð .Ð .Ð .Ð .Ð .Ð .à€Ø
€Ø#€ØÐ Ø€Ø€ð
€ð8
@€ðð ð ðXð Xð Xð
Cð Cð CðAð Að Að ðð ñ „ðð ð ð ð!ð !ð !ð	Jð 	Jð 	Jð rsØorØ!%ð ð  ð  ð  ð" Ð-ð Rð Rð Rð Rð$ð ð ð ð|ð |ð |ðcð cð cð cðð ð ðCð Cð Cð CðNð Nð Nð Nð.ð .ð .ð .ð5ð 5ð 5ð 5ð-ð -ð -ð -ðCð Cð Cð CðAð Að AðAð Að Að>ð >ð >ð&	Eð 	Eð 	Eð 	Eðð ð ð ð ð  ð  ð#ð #ð #ðð ð ð"ð "ð "ð(ð (ð (ðð ð ð
W€ð#ð #ð #ð 'ð 'ð 'ð 'ð 'r   