From f9ad3d108761067622097c29f3906ca831822765 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 8 Feb 2026 23:06:38 +0000 Subject: [PATCH] Autosave: 20260208-230638 --- core/__pycache__/admin.cpython-311.pyc | Bin 1530 -> 3034 bytes core/__pycache__/ai_service.cpython-311.pyc | Bin 0 -> 7871 bytes core/__pycache__/models.cpython-311.pyc | Bin 3167 -> 5674 bytes core/__pycache__/urls.cpython-311.pyc | Bin 545 -> 638 bytes core/__pycache__/utils.cpython-311.pyc | Bin 0 -> 2035 bytes core/__pycache__/views.cpython-311.pyc | Bin 2315 -> 4067 bytes core/admin.py | 26 ++- ...aichathistory_ai_configuration_and_more.py | 43 ++++ .../0003_alter_aiconfiguration_provider.py | 18 ++ .../0004_alter_aiconfiguration_provider.py | 18 ++ .../0005_alter_aichathistory_options.py | 17 ++ ...006_alter_aichathistory_unique_together.py | 17 ++ ..._ai_configuration_and_more.cpython-311.pyc | Bin 0 -> 2886 bytes ...r_aiconfiguration_provider.cpython-311.pyc | Bin 0 -> 994 bytes ...r_aiconfiguration_provider.cpython-311.pyc | Bin 0 -> 1007 bytes ...lter_aichathistory_options.cpython-311.pyc | Bin 0 -> 804 bytes ...hathistory_unique_together.cpython-311.pyc | Bin 0 -> 758 bytes core/models.py | 35 ++- core/templates/admin/base_site.html | 15 ++ core/templates/base.html | 10 +- core/templates/core/chat_detail.html | 43 ++-- core/templates/core/dashboard.html | 218 ++++++++++++------ core/templates/core/index.html | 83 +++---- core/urls.py | 3 +- core/utils.py | 34 +++ core/views.py | 68 ++++-- requirements.txt | 2 + setup_sync_demo.py | 53 +++++ static/css/admin_custom.css | 217 +++++++++++++++++ 29 files changed, 745 insertions(+), 175 deletions(-) create mode 100644 core/__pycache__/ai_service.cpython-311.pyc create mode 100644 core/__pycache__/utils.cpython-311.pyc create mode 100644 core/migrations/0002_aiconfiguration_aichathistory_ai_configuration_and_more.py create mode 100644 core/migrations/0003_alter_aiconfiguration_provider.py create mode 100644 core/migrations/0004_alter_aiconfiguration_provider.py create mode 100644 core/migrations/0005_alter_aichathistory_options.py create mode 100644 core/migrations/0006_alter_aichathistory_unique_together.py create mode 100644 core/migrations/__pycache__/0002_aiconfiguration_aichathistory_ai_configuration_and_more.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0003_alter_aiconfiguration_provider.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0004_alter_aiconfiguration_provider.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0005_alter_aichathistory_options.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0006_alter_aichathistory_unique_together.cpython-311.pyc create mode 100644 core/templates/admin/base_site.html create mode 100644 core/utils.py create mode 100644 setup_sync_demo.py create mode 100644 static/css/admin_custom.css diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index cba48c1dcaa50813a0dba9e241dd2b1b76acd8dd..aad8ca33dbb79926bd5cb29afcf9920680974ac4 100644 GIT binary patch literal 3034 zcmbVO&2Jk;6rb5$+iNGb+r%xYfg}Yr#jS`%Klu`(1PM^uP^lcyURK-PNwVpFxU=g} zBOiRIM38#u1)5_|p{N|V@+WXhkR_s#Dn;Vdo1^ql^~8I-UN?3OiaNXd^UTiM-@LDR z<6rXm9D(-pA8YpW6e0iMAZh7tXK$Ml@_<;xq7HGXMk%o*OLinz)?`=F6gQ=%+_aW< zGg>B^QykUJYFRg@oQ#%SQO69fUq_h$0MqZ_!?= zb0f5UPZVx6JhMA{)nBSeBF{XNH)^52YB+Tuu-Lw#*$XIil62TR4uc1T5sg|zlPs#q zmZT|`42mjZw5!tLb zIbC;stL|WbRM&6U4W~OZ>VR#!Wd}9KXo!5k3~ny#i#Btt;8(H-OytG8)n(sh)%wl4 z7uKun4m0cEj_P+lzZKLBlT}ThvufnZ*;<3=Q3nLrTqKwPX!Wk0@k_0%+oekD8eaUh zMD$fW(+yJ3GxdORkuke5RJiXj@W>mu#G3%%5q=a0V+i92B>*uJc>uy4GR`p;u~w)z zFDHN%5Li)?rL*?bn%U+WD$w-+y)lig#iK~S1LN^2Tw_5&7Ew5jGIQOXZXmv}y z)K)KTCU=xLmCUfzN7pNq&!Q?=LOzZ4pN1|+z|9|g1^_xH*UtFo zn5t)6^BqM=UG9*>L7dY_+LzP4!iCvBtZ->5OMy_P#8_YX>}D}kEGVgZXon7qGrC~d zdi0KYOSZ>EAsR#(4RP;n;`6W^WcNAvi4r5!P2Yn;7%o)O|LwkGAU?RQ-@|tZXhDF* zA@LN%b^JQ|s@j_0E}h$7rcnMB{V?8tOvH{zK^3=M$W+6v#eT?xJ^K!qG)#r{KomHG z0&4})_GT8i;6qR)`UDJrKk4%iuoq2z1U)#3IQO{@D+I<8`#&FvA{-%>?>UWb;UDzZ zWn@N}hUb94zD0kX27euQT3_Q8=MBm)RK`TXT7f<6&tkB+eUoE-5$R~t1{@0%$Gyn0 zh;S7l3m{VU&~}1&%i|v@p>nnaM-}e+Yq;SfC&p(DMls6$D5jC30~WkcKaS78Pxu@N zV+IF*0(N9d={A|(?7z3kH=BpMw@Gz#*u6uGtz&m5?`O8?M4L`@B#EBskb`jlVtf4U zU7UD^uqzYkL{DBN(vd-y8DtrR9?J}}%GW8NTXecjr#q5Lp#&U+^^5J-&+g*H zGX!Kg*^@6Ny5zU$WSdTQB$=M?kb|&(tbL-gixbZfkY%bTpG#N@TNK_-A&X0qW*DxY WYM0L-l}nM!rAP)Q4EZcdxqkuJm$A(N delta 517 zcmca5{)?M$IWI340}xoQ@5t<8n#d=?C^J!AnS+smi6NCSi)~_-EGJ6~LkcU9!#;6> zDmw>Me2*+A8&I4bCe8^FpPaxb%gF(h;Dk!ZaHVhsGiY+Z1X-fVc#AbLB{wtgB_oK( zJXw-SadI_N5*x?~Mck7gGu3f%f|wwJWpXyNBAYOnJK2z3a`I|s7k3E|R}x70X|fja zf;fC2f*(W(0Et`NIhn;J@oAYkC8wYYY{mjLS`27(wCF9q#LW2Q zjKq@o)V%b}ywoC)J(Cx*3-ii>^niGpOhvMj53-AzW3jbJ3ZzO9M4(wI0}=xfe3N-O z)Of&d0df5_xhLmy>UiGbC`wHSx;VAy7ME{+N@|WHD0smBhd38(0IH$CIBatBQ%ZAE q?TS=^MuCE|n13=SmyC?@1qPgGaw^x9$@bi~d<@Kt#vecgIAj1z;%LwS diff --git a/core/__pycache__/ai_service.cpython-311.pyc b/core/__pycache__/ai_service.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f6c1b0497e2551d8b6941673a1816558ff0ebe7 GIT binary patch literal 7871 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 diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index f364545d986d88dcc96e1af4e249558636633c27..8f39e4557a8daa57d9577c7123669f8fac89bd50 100644 GIT binary patch delta 196 zcmZ3;@{dJ*IWI340}yQ9*^#Nw$iVOz#DM`0DC6^{iRzttObn@vDcq^dS?n(2?WengF1R4OepP_i-Ni+88eBT$XQ*D0(%E2lfyL<}i_;Yrrw^R0{7enp KAXvl?R1E<8U@jy8 delta 105 zcmeyzvXDi6IWI340}vFf@5p=wq#uJgFu)FFeD0g5-pQ5D9L1i(9n7G~Gx1&mv!5o% nk8Qp-}P!q_lBbSF-<;db z?77T&9ta2s#uq<5NdLki^ebgrgKrWC8W5XEK?FX{u@eIG>y#iNRfR}~Yy;=9jB{T;jjd_Z9qP)K}5ubvArmivZ^ zvnm0~Yp8^zvq^(WQ?ij#DJ9(c@aFX!R#&T9`xE8k!FAypl^R2#Zf6((!KWNCU|3_cxtc=hqs3V-qd zu0xjVo~-3n%Z*i%$Juy0f1snz*>$-E#3m{tgQXQuI&7Skw9XWna)%bSHAfM>P5nBn zqD=4p$8g>VmBY?ikRm&ZmIu;@3ps*mEzu;>%)emqY05GQgTA4G0eq6Ofu?QD4gfv} zvJ>z(t^t&gDR$Kn3r-uytBnyw?CfyDCBCU0D|650q&=^zL~YSnxhXCzXQB83r+K87S4nI zEVW4jyeSKHmU9i7QrDBhqr%pS@A|&!`?~+z{;G7LCSBN*BD+$gDvi~ov5I%>a9=!5 z^Eh|}rgW}i&pHS4!^1-(2zPBvP6g1*fG~BIdwGTd3?0K=Ah%fEuzGBd^dy#ZsY1B< zY-x2`qOvS!lX^I=XA_Oq)9y1T`fsB!^p|jM32?m8wrGp6 zID=rjOfikp3uKp>UCXb%?`L!+uc_B?7*zBH8nn{DLxwR?pk)6{R(r-=qC y&S|2tigTLigNk$3d6DU@qc)u3nebLm9l=s=EH>Q%eh)K9EnuM*u)Gm$@BRk#2fug# literal 0 HcmV?d00001 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index f9e1f12aad898f9ed85c72e67c2dd8a47e68bdee..b2a89da1bd5e923543c398a167a1f32d600f032c 100644 GIT binary patch literal 4067 zcmbVPO>Emn79NV!k0r{YWZG$d6f>?9uUuPpwkdY&ZL-cEvZ=GS7YR1CfI^@}+NL9k z?vP4iL(b}uLr?^$4+UzVTR^9D7s(;V9D3MekEB4r0s#XCiX3`Vkan@iY2OSbS#sjT z?qKvhd~bf=&-b3ck4D1?+Nqx&X>&08iF~x0uQ_=A1P0$A1u0A(6_^ZD@MU}jHp4Q6 z=gV^if5uP8Y(7v3W`cCgApWzG9OqBA1`QAb-6N7Q+A<77< zU+H;>$VpFI=c7y?g#G(65XhVX^@kpH{%m4dO@8iEtZOA zX+piP=1QQEiF-P}W30(Jb%LI9S5xm9>9uv&PtU_5tPR1>NCT;$da(Cl*$ECe5aU0+ z8#}Tcv17wdY}g77??&QVBi8XTJ2LJ>#w~9AHLO5g$_oI4DlZL>JxqC=bQ14wd^yl zd+h1B<5(g|KnADbD*^vq5pmTA-4>6-?GZ@pRqsc z#)r*788lq>mjg=Z@7!k4Jib2@0-~oJd;&Iq+O|7{M0nO^zHq5u=k&swTwDhi@6SPK zXHCt_tJ$oU(~G986%7y^p#s8y*ys>##Kx&4FB@i7fslC<9yc|>3{x(wL0t8CF|VnH z>xbY0zz72nz&?N&h8v~JC_DiAZ1mBHX~(G-o4r_ox46MItS@PK6(1#E?6s@6TowQg z6Ss7O9;_K|0H&BK#>6e%kd6WOvWx3D0-uVoirhKk%?G82kMn82t>d1-;E}y5a=W>LT3Q!01+=LuVUCwhD5>F}4bE8orqQ3M2 zj0`f9cnftEH`1{;9s?1XZF~%*(`9xeC%*gqANa-Ze)yvmnXw}?PGqKXrI`nQ`~0sj zZats1BbS}XWsAG)X_lm-R%>ha!duxuVeU{HY%!Px{BN)7$O}xTmiIMW?MS?>gNPOO z(M};~54-OtT*xuLcO!i_Y8eBa*uL7vY_J>Gy(5E)-z)x2w(PrtJ}+!?CUL7~D<8-g z4C+GVwyzxW67Jc7tnhBURSu#u3HrBM_xm>eWk2pO`&WBg6P1u>2OK77y-E;-whh>A zlYLw)vx@%?y)i!4r5l2Qv{5u?zeP&;y8~JW9rbsuX=hbf6FI0)^K@%RgcQCzyn4%&FasPmh@6lNlWv2RW?*2G-0Z= zu9vVRD+R3xB_I@vI+je>L{TKUC|#VtF5OYr)0ns@e zrBXh>E|JusD$+(mDysJ+5@ZsTvw-_XdgC-HMBxU7wY&A8t4g`4!SEq)LOtI)RrK!0*)5fH8N`pbB-`)33D$a zeOmyzk`s|EPNLvCn-X0?S(S^+dfHggu?YpfK}!crLX_IiJ=vTh5)P$vV8( z(TLUE8yw4|8>mgf$R{*>$Cy7f5Rg|ad_FwsF@8@)EOUh+^RF;%A!T_B`x)>#AZ=NT JLAg{f{{@-SlraDR literal 2315 zcma)7&1)M+6rcUlO4_v~+t_Xr;>1KwEE7pd2rW%ZOR$YoitU7iQgl%z?bcdF(mJ!F zCN?tY;6ez2mQZjGJ*ZIJ2D?;;9C|AK6INy+V!;r|q2#8J_L5WIjHI>Yn6{7I$IQHs znfK=R-uyH$5JW(wU+)`v5uq1!(j&e*c>M(oHj#!ju82xpiYxIco}+WVD3rvM2xCDL zi&Dv#@|EP2%w$s0UkaoGF!pVrR8SW+c>`^Mr|qs=gi?y;2brn`ZlYN5C0&p4t`F;F zO~zaY}9EovmsAw)5dwQ+{u_o`D__no@$>~Y9%U6be*GjCcx)R$DN5Iwxzm))1 zLoGS9QFY{K8*$R)UU=-W(hNtPaMbok_mshJ&)Fx=H@~9)0yj?g_ zfAZ>HdZ<;8H12S6);U6B>{5YxV|27VT#u){BlKUxI!_p4JN5@W(Nn%fRleX|+Kzb0 zlThXGwW?4*(vl4BJF9X#QorpXsjpwU27C6e)C2q8_D7oBueC0ePr`Zrmo8MF|7-r3 z=WJc5LOciK)dJrO>mnKPQ~>r`uqtjr_P4zo0N5&vE_cU8PdKcCE`?yuEUje9t1ukN zL4Bo(L5g}hZRE@{$rxn|1Y=A<7XZcwL~G$VlN2)+No#=am-M<~f-BKw%as7!comPr z;@U97u2(UgDp6cve(shlEb9dOLCclRYyrA}#ZHImb-F&Na-w7A1=Do7eiK8_NM~0& zWhCJctl|^Qex=hD@NqU{N4`faTI#1M;V@NIdW;UN7r<)hxjOvt({Gj@EIpp74>i@O zqeg3Ud!dns=CjbmZfK%@_s6OonrMbDJE6<9`91ZRqfR_ir*_q;#@N$SwmQ{RuR7}0 z+T3$lZ6ir~uN4{DR3E8(>Og(YR;Qclw4+Y{a^sJR9bah17aZW~!YhFv4E}|H{TUp3 zFl7%%o54vZIN6p^Aaq}9`_bU&X6RAKK6RlPx#&bLHqJCIHO@GZcR}bwLu0MM(bn+s z?>=tJV%XP4z-se<2a!5NEB!*_TvNH?C|7Lp3Z@{>c`XbkUakKQlTAc`_h`RS)(=|r z#I&h;sM2_b49umlNsLdIN0Eml+kivfa?H_Mi&!1$cj?Y1Z9)JEZVL-EV;^o z4e(hIouWF|fz{Bzz(;wATRi%n(5 zQD$s$hF#!dY`~3Zg-m(bOjvmn6L=ylRtM~1amTwdihOKj6ZGv#XgZ`K15b&CX{KFY z$<*|s)d`o?UC*VKO>||n(%93|sTLa96U2_dYUqvsLr|;~^^fp*5YeZ>`T@ z;`UKr3%zgme=Rg^AN94+xZVG?MS)ASk;j6FkF@zg?t?Z0_DX>1#&~Dptia83RP`v% GLih(Q!|m7r diff --git a/core/admin.py b/core/admin.py index 9966056..9f578d7 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin -from .models import Company, Profile, AIChatHistory +from .models import Company, Profile, AIChatHistory, AIConfiguration, SyncHistoryLog +from .utils import encrypt_value, decrypt_value @admin.register(Company) class CompanyAdmin(admin.ModelAdmin): @@ -11,8 +12,25 @@ class ProfileAdmin(admin.ModelAdmin): list_display = ('user', 'company', 'role') list_filter = ('company', 'role') +@admin.register(AIConfiguration) +class AIConfigurationAdmin(admin.ModelAdmin): + list_display = ('company', 'provider', 'is_active', 'last_sync') + list_filter = ('provider', 'is_active') + + def save_model(self, request, obj, form, change): + # Encrypt API key before saving if it's changed or new + if 'api_key' in form.changed_data or not change: + obj.api_key = encrypt_value(obj.api_key) + super().save_model(request, obj, form, change) + @admin.register(AIChatHistory) class AIChatHistoryAdmin(admin.ModelAdmin): - list_display = ('chat_title', 'ai_chat_engine', 'company', 'chat_last_date') - list_filter = ('ai_chat_engine', 'company') - search_fields = ('chat_title', 'chat_content') \ No newline at end of file + list_display = ('chat_title', 'company', 'ai_chat_engine', 'chat_last_date') + list_filter = ('company', 'ai_chat_engine') + search_fields = ('chat_title', 'chat_content') + +@admin.register(SyncHistoryLog) +class SyncHistoryLogAdmin(admin.ModelAdmin): + list_display = ('timestamp', 'company', 'configuration', 'status', 'records_synced') + list_filter = ('status', 'company') + readonly_fields = ('timestamp', 'company', 'configuration', 'status', 'records_synced', 'error_message') diff --git a/core/migrations/0002_aiconfiguration_aichathistory_ai_configuration_and_more.py b/core/migrations/0002_aiconfiguration_aichathistory_ai_configuration_and_more.py new file mode 100644 index 0000000..89d1f3e --- /dev/null +++ b/core/migrations/0002_aiconfiguration_aichathistory_ai_configuration_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.2.7 on 2026-02-08 16:27 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='AIConfiguration', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('provider', models.CharField(choices=[('openai', 'OpenAI'), ('perplexity', 'Perplexity')], max_length=50)), + ('api_key', models.CharField(max_length=512)), + ('is_active', models.BooleanField(default=True)), + ('last_sync', models.DateTimeField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ai_configs', to='core.company')), + ], + ), + migrations.AddField( + model_name='aichathistory', + name='ai_configuration', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='chat_histories', to='core.aiconfiguration'), + ), + migrations.CreateModel( + name='SyncHistoryLog', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('success', 'Success'), ('error', 'Error')], max_length=50)), + ('records_synced', models.IntegerField(default=0)), + ('error_message', models.TextField(blank=True, null=True)), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.company')), + ('configuration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.aiconfiguration')), + ], + ), + ] diff --git a/core/migrations/0003_alter_aiconfiguration_provider.py b/core/migrations/0003_alter_aiconfiguration_provider.py new file mode 100644 index 0000000..917ac7c --- /dev/null +++ b/core/migrations/0003_alter_aiconfiguration_provider.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-02-08 18:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_aiconfiguration_aichathistory_ai_configuration_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='aiconfiguration', + name='provider', + field=models.CharField(choices=[('openai', 'OpenAI'), ('perplexity', 'Perplexity'), ('merlin', 'Merlin AI'), ('poe', 'POE')], max_length=50), + ), + ] diff --git a/core/migrations/0004_alter_aiconfiguration_provider.py b/core/migrations/0004_alter_aiconfiguration_provider.py new file mode 100644 index 0000000..8394804 --- /dev/null +++ b/core/migrations/0004_alter_aiconfiguration_provider.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-02-08 18:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_alter_aiconfiguration_provider'), + ] + + operations = [ + migrations.AlterField( + model_name='aiconfiguration', + name='provider', + field=models.CharField(choices=[('openai', 'OpenAI'), ('perplexity', 'Perplexity'), ('merlin', 'Merlin AI'), ('poe', 'POE'), ('openrouter', 'OpenRouter (Free Tiers)')], max_length=50), + ), + ] diff --git a/core/migrations/0005_alter_aichathistory_options.py b/core/migrations/0005_alter_aichathistory_options.py new file mode 100644 index 0000000..4ef67ef --- /dev/null +++ b/core/migrations/0005_alter_aichathistory_options.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.7 on 2026-02-08 19:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_alter_aiconfiguration_provider'), + ] + + operations = [ + migrations.AlterModelOptions( + name='aichathistory', + options={'ordering': ['-chat_last_date'], 'verbose_name_plural': 'AI Chat Histories'}, + ), + ] diff --git a/core/migrations/0006_alter_aichathistory_unique_together.py b/core/migrations/0006_alter_aichathistory_unique_together.py new file mode 100644 index 0000000..f0f74e3 --- /dev/null +++ b/core/migrations/0006_alter_aichathistory_unique_together.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.7 on 2026-02-08 19:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_alter_aichathistory_options'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='aichathistory', + unique_together={('company', 'ai_chat_id')}, + ), + ] diff --git a/core/migrations/__pycache__/0002_aiconfiguration_aichathistory_ai_configuration_and_more.cpython-311.pyc b/core/migrations/__pycache__/0002_aiconfiguration_aichathistory_ai_configuration_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9939742a0a8910764554c0180db93189c93ebc6 GIT binary patch literal 2886 zcmb_eJ#Z686h58)Wywg!#DIQ`CF3Ce3?^|Xj6v8k$QUOX8#1`ya@MZpGp9RocQV*y zG88E&>2OKqB!h~S&J2Z%6e(9TS7^4%Oe#7txT4D2JxO3}CO?xs-P^bK-oE$tdvAC3 zVg?5FSyH|1&>P*g=+$G{~#a_ zcCWPO2%By}cmc1|7QDzcCuF^^*l;#0(ok6`8M3A5nn`_S9bwfhwsd(HZ4KTmf{kYa z=6)AMr$9u`j+Af7<}^z~e=}nt#&+QNZi8B@qRom}ee4yD~JFw{j9e6uBQLYnge?gvFY>!S4*WoBLJr-N-;2v+iA8OSO?XmO; z*Xe3|!{L=kn_uyDxb6;jpv|E^bhvh8cSe@PlD~FzcN}2fv(iRhKyj4V?UBX)w_al% zuSt}u^>0E&yy#R#?bz-pxW|qZ4g8Btrg?1dGMPbxAIao#uG!OxYiOS=eadwXc69d7 z(h08hUM~OFEFG?8cjp7D@Fe&6Uvi$>Yi)1FOYVhUbP9InX&SuQ-jT39Jw@HXG<@#d zx$}~uDV8Fu^gymKp=(P@sp@Q8Dk^Axii(BFtcQl>s-;Us1Irdhw5wt$W!YGh?%_2W zG_e8mm31(J4;5>ThQ8|X zuSOt8AodrRb)|?+I7#G(l8UvGwM_kTMPWV_IFy8}%jg4L8dOY4E?UYe&bp`{;U&4M zS~RH2rX`tcS`mzS7gbriM?G3qRcWZT3Q4wLg0nD9e;~;Sfmgj;ku``&R-__F!(0zD zu+Fw5SvL*pngj9MF<<4pM94!C3<9S9e zQJ-naR@G$an$;qJYf}H5!!VGrVdw_+=6Tm`U4Z1xZhNIW=6q9I9wV5=$yJ}gQ1tFp@AVk3m1G!WK`fP%qp7TPtz z>+L4HJ`8rs)9_WLl!Ho~V&Kx?#IkHSZ1}3Kt60{YQDhR5G7rHy!_X8YP$_9Qpjv%s zMb=6>711IM@GQa|Mg0@Gxry9lo(6IVIgi(aLZG09zV2NcA=hWjtJ6tZG(u7(c{Ch}IZ>pd0thid@9QEW5++F2`Zmf+Nxg*=FN+ zH3OqoDYi;Rk~IV!GWe*?DajKX@-Pg|C5j%NHt>V zKVpYS-^r(|cHhY@&+mg|K_Z$#`pkNtY4@2VYBgeUl036<+fJS#U)&%!6*9U)9;}h% zdOf*rC)Y{zQ6qMgBu;Nk*@@FzZjuQnL#1bTReq+f_o!=THsmt}$Wjl45 zM6Ync2osEK^=?m+RH2?K*r@`EPB&sn(tlxN)$YHr-AnqX>itu8{}hQ{<6h1%FK4!V zFN5~Q>-NR3?TdHFveguXb=P$fx@19o&FK!O@Fg}aHV*`PyPMvwVCb~~WQURFVIi1c zAjBJeLycIb(UW;PWcOr#8vJE``}S{l?EEb|f7i~Jz{CUBHD-H8Tx8wBB69&0TmWtQ zJo2bI~W4)4!qkQlFHXpc{`OS(W#GOcvmHf zay?PD6J-+BxakYb^o6aZmx?_;Yma|pk4prtlk}r{`jMS}M55pAi|g@JkMHp`dyMIJ zL1umQh_l(wqQ$e$f55Zt9cNqtH5%;5`G)6v+^OgSv%*c?0PbyI%sDm2jaeA7zi8$n skfz(^azT~528eLX`89+SWWQh2H|X*;g}vly$c_%aW`do4q&$Yd0F&1a{r~^~ literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0003_alter_aiconfiguration_provider.cpython-311.pyc b/core/migrations/__pycache__/0003_alter_aiconfiguration_provider.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d72a5dc01e0de03c7dfd4654f9ec549be8a11689 GIT binary patch literal 994 zcmZuv&rj4q6n@ioyKMteSEC1`A{xUch7!CI6Jpe0_+?Cp?xmK_usdsiIc*g@aqz&A z8+Q*F1Ahz$4rH#LxRt<(lW)3PjNr8W=6y5o&3yAse~ykifNlO@i{I7&ek)S9#xOXY z!C(g{P!d8!IwaCM8UYPn1J%9*)fwsQbz8tw|^1UE$|Ei8EU8_2v%(=qiT zRekMJp|nzweBqRdwt-uiNy=iMOLGbP**WD%%0wEnb)IiXXZa#lxe*f~kH6UqRXuJKIzP$AEcp49fS&1S=q)}mGjkAz0;-M|C8uvWu zc&M5dA;WmY^F9}TsB#sf3>8uq2b^U>-KTVB!<>m%Y87Fq0sBSIA8_g5xrX=p3r>~o z=PPb6iI`ig7I9vk%yPv^8txT3vRLpMKY zCl#JmST+sYIRS(-1*LAb%*{9I5o5KC#kL`B+V|sbGD%k}t15d2LLaEKD)%8q=hn=? t@8{wXQi5N$f>7#&5EM_^2QYEPmT(oeox|&oPt?TOe`c$F{{XRp4sQSe literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0004_alter_aiconfiguration_provider.cpython-311.pyc b/core/migrations/__pycache__/0004_alter_aiconfiguration_provider.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f725916ee129851458876269dc3764315648449a GIT binary patch literal 1007 zcmah{J#W-N5Z$$XKHEwJ1f&!QbQ}>nODK+jf+9kS0zn}1AwVgvF^+d~=fhvDZAen3 zph%f6Jw*^oDt-w3q6WshIVhgGi8A+i&0O%+9`@@z?QjgW&l7bH{5*g!~dl zHEJU=m_@Qj7-7^W0d3PjYD<(zONlO0Kefltp6J>ZFX@&K=jv30i_c`hVaXlKLrA?&1GQ$|aa z+76una;3!Dx(7a6!V+k9xo+FJVcQs&`G9=Pwm)Q!FJgto0MBI*x*jB4bT>EVM1>Ya z7Osc|eZK1q?Nafm%iqXAG~sO3>P7)r*;*E+nFSl*W@*H&PZ58g#EuJ=sJ->)@3627 zkN(qz>3EZ?n7jl*k_2J<{3%qTKQ3hHhPwT#Fo=G8$2ih%9Z$B7uiZSJXzt!SnrIe; z+)t$oI494`xODxg+%NI8#Gn4wooTbfazEiyXj1%=RfIyOl%jTO{vfT(Z=sG+wIG*a*EqU5)fbM_e|XDt Fe*>9=61@Nb literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0005_alter_aichathistory_options.cpython-311.pyc b/core/migrations/__pycache__/0005_alter_aichathistory_options.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f069cce9eb1809724fc81a7ec4c0bf9a333771f6 GIT binary patch literal 804 zcmZuvzi-n(6uz?)Cox2jHi$KaGGw5RM3FjFNQHqPY6?=fEXT!nI8Gg(IopMH%D~W( ztqTKF5h{O7h7Q$jh>6WrI(6cmois>rcYgPt@4ffE_uYM~*KGvj*UuyWD~4Sg^HH8D z(+yCL5km|IXoPzhBLh80%=mpSg=s*=Km zD4URxU}?Y!Q8x*BpVA-?28;2Chm40Vz(`H1YHaE9yMpoHb&*WjBH}YnzoV%p@@m%^ zh!JIY=Lhi*8b-APYa29vr05gvl%q;pRvx?WE1F3Hy7y7c|Z4l$@ znOUf|k(ox*8gFMdsy#Te&#J5Ejn?_SN0XKHN%!rfNnvGWvu0l)5SLnHW_igPztU4S z=jnIarmC?a4Lz~O`gz+0_OkZK-R^*PRoyITdm`_G2@e;=Z$M-w#u)0sD-+cEdu7%Q WY-Q+Q@!3ARy?UV!F8_0yBmMym=-Vg& literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0006_alter_aichathistory_unique_together.cpython-311.pyc b/core/migrations/__pycache__/0006_alter_aichathistory_unique_together.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fefd9433c67b7720140f31c4f64b33fd5782e8df GIT binary patch literal 758 zcmZuvJ8#rL5T5lbXOk0;1kr$WNC8J|DN;n+LPO{1Ql(=WfB@mK~$EYdt|Lh6sz z7>g|g`@msD7+^9PlaXlvwZ9jhU{O1_@VJ%QW6CIW5GP z7ZI1+{T0=rRQo7Z9;Jz3W#Lht7MogayW`{hoV7NyTW^034K{0~CD&$A%pz8=*7y +{% endblock %} + +{% block branding %} +

AI Chat Archive Admin

+{% endblock %} + +{% block nav-global %}{% endblock %} \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index b721411..dde5314 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -12,6 +12,8 @@ + + @@ -80,6 +82,10 @@ padding: 2px 4px; border-radius: 4px; } + + .x-small { + font-size: 0.75rem; + } {% block head %}{% endblock %} @@ -122,7 +128,7 @@
{% if messages %} {% for message in messages %} -
+
{{ message }}
{% endfor %} @@ -144,4 +150,4 @@ {% block scripts %}{% endblock %} - + \ No newline at end of file diff --git a/core/templates/core/chat_detail.html b/core/templates/core/chat_detail.html index 8cea243..cc3063e 100644 --- a/core/templates/core/chat_detail.html +++ b/core/templates/core/chat_detail.html @@ -4,40 +4,45 @@ {% block content %}
-