From 83c8af9b50373577facdee255f64a2cfc96808d5 Mon Sep 17 00:00:00 2001 From: brendancopley <15661730+brendancopley@users.noreply.github.com> Date: Sat, 22 Mar 2025 11:57:39 -0700 Subject: [PATCH] feat: porting over python to typescript --- .prettierrc | 10 + __pycache__/analytics.cpython-311.pyc | Bin 0 -> 9939 bytes __pycache__/analytics.cpython-312.pyc | Bin 0 -> 9031 bytes __pycache__/client.cpython-311.pyc | Bin 0 -> 13292 bytes __pycache__/client.cpython-312.pyc | Bin 0 -> 12298 bytes __pycache__/complexity.cpython-311.pyc | Bin 0 -> 7779 bytes __pycache__/complexity.cpython-312.pyc | Bin 0 -> 6616 bytes __pycache__/examples.cpython-311.pyc | Bin 0 -> 11766 bytes __pycache__/examples.cpython-312.pyc | Bin 0 -> 10178 bytes __pycache__/format.cpython-311.pyc | Bin 0 -> 7172 bytes __pycache__/format.cpython-312.pyc | Bin 0 -> 6229 bytes __pycache__/reasoning.cpython-311.pyc | Bin 0 -> 7377 bytes __pycache__/reasoning.cpython-312.pyc | Bin 0 -> 6933 bytes cod_analytics.db | Bin 0 -> 8192 bytes cod_examples.db | Bin 0 -> 8192 bytes docker-compose.yml | 21 ++ eslint.config.ts | 68 ++++++ jest.config.js | 8 + prisma/schema.prisma | 1 + src/typescript/__tests__/client.test.ts | 49 ++++ src/typescript/analytics.ts | 157 +++++++++++++ src/typescript/client.ts | 294 ++++++++++++++++++++++++ src/typescript/complexity.ts | 213 +++++++++++++++++ src/typescript/examples.ts | 147 ++++++++++++ src/typescript/format.ts | 96 ++++++++ src/typescript/index.ts | 14 ++ src/typescript/prompts.ts | 65 ++++++ src/typescript/reasoning.ts | 121 ++++++++++ src/typescript/server.ts | 79 +++++++ write_files.sh | 1 + 30 files changed, 1344 insertions(+) create mode 100644 .prettierrc create mode 100644 __pycache__/analytics.cpython-311.pyc create mode 100644 __pycache__/analytics.cpython-312.pyc create mode 100644 __pycache__/client.cpython-311.pyc create mode 100644 __pycache__/client.cpython-312.pyc create mode 100644 __pycache__/complexity.cpython-311.pyc create mode 100644 __pycache__/complexity.cpython-312.pyc create mode 100644 __pycache__/examples.cpython-311.pyc create mode 100644 __pycache__/examples.cpython-312.pyc create mode 100644 __pycache__/format.cpython-311.pyc create mode 100644 __pycache__/format.cpython-312.pyc create mode 100644 __pycache__/reasoning.cpython-311.pyc create mode 100644 __pycache__/reasoning.cpython-312.pyc create mode 100644 cod_analytics.db create mode 100644 cod_examples.db create mode 100644 docker-compose.yml create mode 100644 eslint.config.ts create mode 100644 jest.config.js create mode 100644 prisma/schema.prisma create mode 100644 src/typescript/__tests__/client.test.ts create mode 100644 src/typescript/analytics.ts create mode 100644 src/typescript/client.ts create mode 100644 src/typescript/complexity.ts create mode 100644 src/typescript/examples.ts create mode 100644 src/typescript/format.ts create mode 100644 src/typescript/index.ts create mode 100644 src/typescript/prompts.ts create mode 100644 src/typescript/reasoning.ts create mode 100644 src/typescript/server.ts create mode 100755 write_files.sh diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d1f6bdf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true, + "arrowParens": "avoid" +} \ No newline at end of file diff --git a/__pycache__/analytics.cpython-311.pyc b/__pycache__/analytics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c940d63eec112777c219127f7a3622ba346d058d GIT binary patch literal 9939 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 literal 0 HcmV?d00001 diff --git a/__pycache__/analytics.cpython-312.pyc b/__pycache__/analytics.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99200dcfc5369e1fb7cc811b62766d7ca1f364ca GIT binary patch literal 9031 zcmds7U2GfImA=E_zbJ{4Xi^d-Yi!B3XiJom$Vnr+PV^Hgwq-eqvr*oIxi4}I`s5uot``{F4Dkea%vi!~O_n;ZpAcF~7D zXNE)3G~*__=mOab>+sxr&;6Nu&pqEc_xj%|E1d*VFa3w9ie^Io9WzF<Hod)yIn#GN50h4SWDMcfr~X}l#? z8LtXeX}l>`9jZ1F!F4k~Ovr5_!-TiKOL&{$zDeuFp&G5!4yBHprhHlLoGIX(PdWP% zTx?E`ge682#B-6bz)U7ZMxGX!foU$1V3L!}pvX$vWd{$y+1QCSe zTmo9f1zE&iur(i@OUIHsODp1Cr4HDWGWF3P^z03 z!ZA+dR3HHRm&1-4yPLKwvLO$i!12nm2{au$~sd z%G0;Zg|)J2Wp=?4tu5|I=cAq{%Whc;Z7a04uA49xrch-`zN#dT`)Pq4HRNGOaXy7S z?B!$mS}k8!sP~k#-%*nH>UsD+YhjK)tqi|;yR2R-Yj~`zan1&tR(&*~?QU3jLkK5D ze*Rh577mx4i6?m>rYjI?2=05t5H8l1pjuNXXGN_SX&g4KVn&R_IdP6XE6hRG#tV~N zDkcMKPo!coZXza(2K=g>WfNRnU|H44vM^>UhP;bq-$`+?e2tA|`D7T1D_It17Zdby z5qDq2W2u@Vyl6+p&VyRXs8-gL98M%>RR`uInTyYeR+Mmp?m`!FHo~hegO`Q#vTEg% zaX2Qb-Oz8! zQP*&B@9L4ok&Vi_<)NRQSsdD^-myISvwvEA6=iIzEg(}{f6=$vwRn7^e&@y1KUdx} z@7VA*UHrjnV)5j5Gc38zuX~?^LTq`xZM~)o8p90aEBw0qz>klL0b|;ao%Ou6BU^`| zy5y`oc=}x`WOfkV$(w<*@RoN;$jUpweo`SDZ-bnj2df!!fUT@h-33!Qrd!JS7xT>H z8qXzoMqAPOuh;+4urV{37eSpwTfMajWR0Y$MMefrPtjsYqs+h+LkNC(6Hgl_dV!Kr z+q9IX4BL<IsDuyiJ37p!RBM zjtW@j4U$=MO_qAU4eC)sBU-uncGXLDAorZ7)^lKzHFh7OAtx4E~ ze~Jj?uOSZk$Gucix?>56D&Ua;sVU1^L<5s)aOz{ZzB zePtBaqa;DAx{@NuZnqjEt=RKZWoF5F`Ykd`{hFSo0_OQ{9U(z;6T)l}6l&^R#(N6J zv=!%l?GEsdTw)VUKm?zXh&z$AAYqW~Lb4l4E0Q)Os7vBrB>R8_oZ<`cqtcQrsx&tt zspe@;n$|44TFZhX7Cvi$NdmMPWO!b+hLdpsF0me)TEej;_)=Bk_b_M6<6gO`s8x@{ zn(8QETG8ANYTe}hGVR+`YaaX&YnaE9eumt7JshOG%Q3&}iL+EH(gl4h6 z?WE4P*0IvD8eFn%n!JvthYd|1j(z00GWDzGOmo-m?Xtwx4S#X1h9nZ+BkXUVo%optm1$6_VagMTK6Bn$4}=PcYo1P>8)BC+H4|~ zp7)PmIDXN(?rO=o>fRr{FuLw)%GEWjy}0t?11LCg;l$5Rezi2LGk=qCYkyMH{|xm( z-wXXsmC;ApZ=j9Zq7l5?-)L|}tAtjrBpH2_9iiaC+|2J>)L+sZvPJ!W z>-{xAZ-Iuxy*o`u(Lj`>r}v+x@L(EW0sYWXM2(VU9RCG#+B`_!w}eeoBy4*76d?^4;;@y0kWh|D+HJ|i9HWPRLDk8fn_~4y58^=N zF_yG*;cyC~;5oq2WGW$#2CPNUi9VlNVF*JQD82?I;_E;H9v!(Y@1z7VACGOrKUyNd z>T^??4*(XFn-F5UE1+RN_MpSJeM(HGX4r{2)jAo8$q-P|=)mI0;z=ahi2cC9%n;)B z=?x(_21K=qf&|gGq*9`Us;uL)4$KYUG_yq>3=9MDHip9U`=2n?Un@Hfy-GX)g9g

Q_Fb6%;73_sr{e3p^`mUpu+lZ0sd{y@npD<4 zss+@^)i+;uU2=WIX7?OY_8hu%PTBKHrhaJI^1$m~i>yRe&n^u=@{q<|TXjxP6*Otw zwcH2ITbRq!m!@;fu4~S#&fMk?IM{n*vFeR%bxd2qQ31FF*xJv&r=fEJf2#xerLax{?^}WOWSzr>>$`K7jMQ8 zbG`!HMzEH=Wgddp1$zQ+co*z|ho}Z6$p}gTTM8IYf$qG=m*HcIB}RMv#};B5?8)MnTgZh6{Rb3gbHr6g88tU;ZK3z@;bxT>@?z-^5yk9TCm7 z9(tZe4jl{CDup??dxuNQnHjiZ6g50?=y-xq0++ZNEJ)&8m~-kmR!q+7bGGH@tOBD= zR-6Di2@k%GH$aOZM_VnpGS06Si-;_J4dj%Sm9Y2nki!7vc$?SaEAfv`X4`s|w%&~Q zP}V!Bcn34yp{)0q;ysq}jw}u5y!A`N4|ncacIFyduk>ANUbb#piGTOy&P$!Q%pcot z*niWKeP%>?W+c;oBGWjUZ9Jtkp1SX8&3g7L5P(c7o*pgNp?ErOTNKa1Eeq|h{nAS6 z8dmnMwtsG;{S5Tj!fe_|P5q|xAKJIafARHS9?Ere-uB&jL3yTM=@|H}q#QT~(b9th z9ru8`eQQ-KRk@vQxxJnEujA!M4hvKJS7NED+uTQ5_HOnNkN@if+#x?e&aNRV`Q5=9 z;6Jt24%O42*3-b3$CqDxm+)oy{}a9xVNe;QD~CF8>zwxyjQ7TJ7S^zq-(gHLmtgci zW_@gcP-UXZJVIGTz&TO7LPczYD2e7RC75+Ak6C46M`ITXnDtbd$1uw}-#w6w&v2r^ z7=aBlA;`0WkkBv+Up)Ynir;xPyb|9AgF7mo0cyaMOEdXKbObrD`$n$L;8^1kIV=RK z%ORBZs9Oq7dtn%H8=Nc=DUp;##7I#}iY}8Xm@*;MOX79+jW>Uj%s&5` z^89O=&et<7Z)98EQd-`68USf^LB$iydU^nzZjWa?y<0XjQ~M>{pK74A=^!=Dn=bNy zGfGXp0Xc2mxwh_&kvEmMBiBdn9R7XB@V%r|gg?6we}YfNpJ5C2!BE9eH~F-s2KZ0A zYlltrT@wv_fEo>$Mn~bc9Pjl-^t;6!Nbu%C>_ze_61;>Ie~2WCWCjVi>_mTe+>cy` zL3RTp9RPw4rvqDNyXAR^!;bV*TNb~i>GNKzt7Vh<9GfAo)e`~y|{Vg0TBh$w>y8hxn`$RxPHB`qk{{?}cFWY}$qMAM@NVRYOZyfwivH$=8 literal 0 HcmV?d00001 diff --git a/__pycache__/client.cpython-311.pyc b/__pycache__/client.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd269d0d151bc1febd02f5afc7919f904d1ed7f4 GIT binary patch literal 13292 zcmeHOYit|Wl^(v|dQhT7Qm@g=7HvzGWyxt2+lgXJw&Pea;@Gv7#LSp8vS{<6+!;ES zN_O3CP!MRaNCN{_x9YB1q^aB#h<|heW3i6_`^SG4Acet-n1ulYi(NGT7&!T{{{`2bXVTSoHe9Iq&N*q=mT)EAb8Z^8Cp<~- zoR@|j316~du7QS~iN<8pTvM`nt~u$S^P3nmBiKKI+CSCW#9-VBhIhTs@a|8{@CAPA zHP^zM&oL3t@{ioFEeffrQ^T=za*>nbR}umnOT>kg#J)$k#YKUz^J&6L3j%v=fs3cu z^gMf4>nH;9^1&OTrS=YFIACvr{Q) z0aC?dm72s6_ZQf#*@#K;B+?uo<A?0 z)1nkla#ET=bmR0*4&#q=5_g3YF=fL{n!xAj6f__vkcdTTeqG?ibSj>@dQM0PG03Sj z$B4j5LNu1ozAG)jbvU5|3ws#k2uG%q9+Hx?+RuL~)0HsV)YoF+mPmo7~d zb6Qj!AUEP9ol-1U1xc~1T_-rdFjGaA5bTK3iXu;yBT+#UB*c!N+=41gqWl$tW2Q8! zjgD6OK!_7#{rZxMikdiS;)QuGlaL@rWvb%TLJ_NCg>>@-2U~HXkcwtVLa|?ek0Vz_ z5nG9C>&cP*=RvZH`xoL%+?5Q^FYOoOk|6HCLWC60rDADl_R{_s%H!em{4kI5c)!}! zk;Ns&8I8ta21KLF9ouEei1x7qY7>74^V|vO?rhlN+-B;@0UFzvC1rC(LU1gu6 z*Ij0SV3MwKD-$}rGA?(F7J@I_n*jm^`||Mj7~0L$h#*{;KZf69D~RXQp!)7GS+m$^ z6h&JF%3qtZ=A1QaS<^)fma1L6nYYv`&794Q3s4jPOxBvU&6{~E2sSUo>*9>JR%JsR z7U{>PzcKxg;cefuTwv~4eq_zrdAp>G8xVxL7^@XsKVyDw<{d`SCh1}Va#e*1H2G7l z*~}Kv@|3n^?Yy(vQXZ5M?^(56H5tWZ*1TqDsb0wzA-kncPQ#MON2us(Ql=dd?|WoRj(+exgEYl0Ad6 ziIvi*ZrP#A!e{3JL;x~USAV1?pNcOg**5@AK^VYR60?ix#0{Y);hsIJOqgV+cpii~ z71@g%;gSOAVQg+Jk>NpRvwS)UVxT6o{_?sYWk_mE+bet!r6q)VItu`>iHpFAYCD$w z01*HVv2hVTzMF{?fmiHmeTw6%kP--&Anh<9vrnvmo`w~7wK&B)9|v&@l3aXGAOzP8 z2tO&k2q4TV2{$Fh7EkfQO~nW4;^IP76{U)Y3V8rynUr+bqWDyfRYX4kGrhf9lvhei zP>-q`ltzO@2Bjk^PQb7v&BYcXPQ@ID_NT901Kg)LRMf}Q_QTsk8o;Hf*fJt_RZv`d zG2}3`JmOZ|7q~=5m?k7m2(ULE6tu25Pb)9ak(cv!KWEq@Y+5 zLQ1iTi;1`tagbe*RIS~vc9INXW(U0Bydbc{U=%xy3Mdm1*E&u-xJU#)c&eNbm%HnT zdBgV!$RM7BweWlRyTz1)OjBE#F}V&t^mi7z4ix=kvVW}L9V^?JmQdaS4}b7Z_@nU3 z$+dW~c~ov5&D$X|c;G`xZXYdqTS{Jk*=7rZq(%a{f(8G9mGRXh>+N#KfwBW*olIk( z?9vl4Ha{k>PoQ`|(=zn)*46M&J6Agk{@rkw+WH>0hkri4`utB{Sbd?`zFThJT?&K? zf$>|Xi492_Zy!lmBbWrsD?P-avTZ2a2G^bM|E`1M=AeCrpNKfYY- z9hZB@OM`pY&&Y$5583{;nP0#9%U6Ff`|)g%Jt(sW@6F$TTRwEU$exCR+OQxXUM9>I z*aLECtORu}%fUV6et#46L?tda*$d3AuZYfm3q>U9$^*ok`F%#xRRRNQtX8}p-y(ds zsVQsLx<&C>YfAvdXIV4U2wkXLJfP0HN)NFKy(&u2QBit&J9$S{02`E^)1dTRMpd?x z`l3qDZB!aki&i%%JzW{qs><5_J*{oLhxdZI@Ksy;Glp-dRUps?3n&pY-^}}|LNx3M z_?Gu!M7EB>mip~{Yqfmieu58t+uT&)wXItC_EkG5wIH|f4N48Se}={XC(>+5K}5+Z zPU5apVi)2d>&X&};*st_5}ZhPrG%}dWI2(3sE9jVXQ7DulWD(NUTs%_PXcEu{bD&H z=vpvl1dOo98Jn~UV=@}7Ds0J=AFIFXKV z3GwKNp7tg-_9Y+{b-ncowzDwaB-pt zQwg?v$7m!TvE%_BULKbU!c3#B|ZFt7h&q5tB8-d`PCH+^RNjcq-C z-(MU$EDs&t9C~?U=;h+jae3%?abQ{=n1+N~Zk2h@L6H$VIfCs&VL@I*f&iMlgk%ax z#6yn3L$SiTmJ}xuz`iO9ygDtgITT`&X$;LEIf3LP5XB;9l6Q@`(y0k$6Uyo4t6KERVrn9cQ?i57UK=%1DSvjy)ge0e%B z4gHCL>F#d?rlIho&fTTHp+aO*?t7uo6NUTp8xK{)Au;hUccH~Oo%f^S(h1FALP zPO@zi2Q(hOC;bVjPaQY(|< zMvsZ=2#GjVnD8!^LP0Kt2^8s0L7dU`pjo&`1}kz={6MZ^M%2_hBZoMCy#{0(03g>P zwyyI)n>c@u*?tU?qiRz6|AhfsyGpG+Tg*{_PrCX`J%fc^&&fUCDRjOC_vbSYj>^a9 z)E7|t&8YN&SZu+DuN#=&fgMyh*t6Dsa_=F?g0?6mZl*u79+vyYOJR1cQx1>RGeyxA zT4RrDfT5yVeH3gps#Z5{c5niySM9bXXQkdo7-IEOy^T4W(E(E5hLi=jO6@iYoFAsD z9Vlzt!QZ>Z&P6;~n{lU{bMTg&Gc^b;(5+5ucCCHJC$=DK{{*D$rzmOZWo*M+ux-mZ za!x4U;Hn|6tb@0yyL18$hMX&fDeP69nR7wiD7xsi=F~#QC)DhCO7Go<*w~n&I7ITXgRKVwa}VR3mKm~a^Jx_YumkJN^lfyN$KG| zRW5;j4Qy-S@4f#V6lXYkkW# zWE+fStTh``YvC=ky-^y}zJSHGp|(W6`8pjD5{8nSt37*`l2az$pKF47)Kbkyju>Xa zb1-@>THR}{TFCg!HfCM59M3h@pucVZ1okPP;$DSb z#(897Fzp{)qXTMl@j+^D4RPo=(Q)57^whDO?!3#0=!3)4QB(^rk~kbC0t8q&X`{x1 z_vx9uiWO7n7WW|70rct-cB&4zN%m~Tf2h8*LvW^IVINq2d0`)$NMDV|_OXH#8>w|b zev@Stk7sGF%bHsCm%2X1`3m?~olfg_dXz|k}?RT5lv1SfR-_nKcfS~=Vz z5?n+CRy9Mqks!z5$P5e~_?XXQ4B~cbj0Cr(MBA|#3M2I1@ji=@-d}~qNpH=1!VLCThdI>;ebe1+yyS09bM&$%$aTXgz`RvqtavHOP zwM=zkgHt(^NWgGaUTxqxmqf~Watm|kko*-85oZ{#sx23FvJ$knE$^`aQ@!TiY&(*QHQ;X-)7>=^~v={kDv z;^%>liJ8L0Oexf#edGx>tKPLlYaJ3GkmT6hkNE(1}9J);sTn zq~6vKGaH`Hf~WIgVAtAv_w4uGpPwuS&d7l?`I-F8e^(-%#lVytn99$nrFY1o;my$a zMrgd`XE*%=8~%Z{?-oX0Df&;#{?n!Q!Oix_Mtfv^-+fE5{jl7AxYQC_8IoHNh~L|F zzwLJ;g|;cY1230dFbZEXwy^svB;by2-I+j>o0w3q92{D2mVfdMr z-*K^NuiUgZZ+p}lTIs!g^3Lo>v&Gf{xpg4#dN??=9{9!F$8-03e|dTB@`r&B-@P5Y z)BRC*vAI`n?k&Lm{8{QzM@H|FJ$u2|?CQGbyg$7$I#n2*Dg`^`;J(e^iyOfgKX*Nt zECw&g!3+6QTKv(C;L-a_5B3y;=jGt}{Hgq@N5RhgDP%DexL5YF^5hKJpKtMQ*+ zU%kHmO0oOU{efcF(fn-5-*$Us(|=&Yf1niXDYbW%LcOJq!H1naYrakPAozWX?1aot z6gv;eorg+2!=-RPqU6T`5DLDIssjD5*8w7}#7Qf3-_-$eQtHd5;B zd)U#vlH2SV+vpi9_KeFtEJkNSadXF(cC;CzC^$MggqScT+dSwaDG2`JJNU;kVO0|U1p`!%0X2BZ8<)Jzfr*6tapwmQjq^Tn3 zQ6O~t@@C-3M&QW(`C?#N4onvsx82TZYL=)m#~^blLs8;AD?@8h+ctKNAUo-?9Kb_- zh6040S-ORf>M0;SADkcs#zah%k0NSv^%xAf4Z-DoJMgP+A$TTC+y~jV@vInlQ4YLV zXxw%?U);_yoPxOLqAnshYRG)s=?7TVkOwB9X}RP4B0zzfGrJlV8-Y258x+N!p=q=! zg=({AN;RtAb-lUVSBE)U(i}yWuu#%1=8@C;fp62-97Ml}v6%cp3?Qof?CF7$u z5GJ)`-ZYMpwIo$P@@*3)-9}%sm~AsAP^nv~(` zkeoy^jRbd$c;XKK8G_)9qmqW6W>)bK?8O-J0TMe+s-9A&N0wEzgC+_C%%g0qW$hE^ zVRg)olD~t*xVFUq1yZ({OeSEh$qG#e;$}?lTQ2-7F`irW&k$%TFqON+bQfyxN6fB5 z?OkH_6l(7hGf=3#OH6N}_AW7J3OnDR{Y>6lo*($$_ucZ9tqH3McwV1=N#noT;VT`L F{{v@WN$UUr literal 0 HcmV?d00001 diff --git a/__pycache__/client.cpython-312.pyc b/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1345f5184f3c5a640c93da0094b3c311af59fa0 GIT binary patch literal 12298 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 HKkmN($^2{a literal 0 HcmV?d00001 diff --git a/__pycache__/complexity.cpython-311.pyc b/__pycache__/complexity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc81ac6b9f7165f68040767d566017d502d5f0d3 GIT binary patch literal 7779 zcmcgxZHybodEVvs$B{hi#M6E1);_5gCEb^0HocnjiIe=mG zN1u23apXyIu5i=U-ox3M-I;fudEc35M(>A00S?z8=YOQEevbQ3dT3s*Mq+&z5^r)6 zH_JJ=6P(1K1xYq#mR2-b z5HrZnFhw+F;U%3(f~uqy(-;pVnzqny)3EhDczBbOIT*;zIwXG9DLKw^vo6Uixz6&l zZv49O>ybP#-)Hq~SpU_!@jA;F0s^kaGYq2+BsO}9UWiR&w`8kb9Wk+q1tyDzt|?kd zFibgvE>W`yzO7$0R!;n_&)MCQc*!9-C71o{M%p2HF8B*vy!AcY>cmZ z(E6&IfE;MmOW*^<{FV#eFmJh4J*6b9E^wIEwYVW`hHQB=^EpFF7?wXVFQ*m5WI4+_ zr|5V$XSou(BwLALv#GH`^S?G*HtR+QN6%#ph zOxF-T$OL8`1Jz>p@V>I?-X#dsmB)td;SmgYk&Z26g- z$TEZM@`D$2#GFrCfeqicy7UY}T*=q#@+F%Uxf7~rfMk8{1<-2+6>T0|nIg5_MFgdF zmYGLa@?l9dML0amCd@38^T9bqBT7sr%lW{(Xv_zrQZS(tUfX#PCO)bPah!{J8BK94NYG6XA_x;i2<`)%j5(}r^=T+h z6vgW+fEC^F%eaDg0!2DAV!Y)tWHo8f%qD#P*u>L_T4Q2f$%%7WNy<$aiYXfta|mAv z?x`bWauW$!p2qa#m_*Cd#74#A7;0Y})hlK^o*$ZStpcspV!T<}h4ze}0AA+Sd|b4* zhI?qZwxh4sKR~!WTI=0`#9B{aG_b}2O0KGJWNpY54X<%62)KIbu?c}zf&EIr)kmdG z2sj7u*aDxE3R>WE?x4peJbM4{p3>9jw|s=UW%OcN!^?K-^mVhO7|5*+9O+RYg?nO;XB&uL0j8@OIr>pS`4+bS8x@4 z1%IKd5V`~pxzdWEV%Uss^rhe|gvo*MA?i_@f=Nn{+G<`uZWjn_LQ0-?M#1zQ8Zx z`Od3zI{ImEf&Y=?eE(_gGIyD`+*s?B%w>n=7@xFw!ysD(fjy6(&jW|C5Sz5x9LYa= zx3xvC?@8o1y;I+0jUQHZh+5+a63vgFL1pW^0WWjq?Z@i(;?#vP1s4w0{3n1 zO`hk@4@w+};ZG7Xs{WG9E<0HUpIR>3@Y!m{-UQNWBnU6pdGa47sdFa7j^q#h?`dj& z`z)$7CIO%Qn)@|TwNJzSp=y^+6kxRT!@h@{FT)J&A3K;C%azA+AMRR~R#G?9*VEVZ8+v*C ztH|4QH<|G(P}agxE- zi#2st6`>W_kc3vKF398cU6>VVsY1jL_DEKs zm4R(%D{XbRlC3Wx=Mj3?tTw9hW7CQ~0QPUzHkx&eI3^t0$=aFLKr_`AsLfA94e8pb zDFmu8#WhRAqVz%*(p+k&S`xv+^FqgE2Z7adm&qXi;1c74eLr+D^ls$6NM&fUIyAZR zP-W=)|w02R@1(Sb6;B*ROy5+Q}OyKlsB+^h7m! zq7N%)6)FJ5`Pxz+LMeS(>VJ?|MD* z*?$f0Tzqlq)JmeV>tJQ%(5>mp;IZo9v9hnvZg=secZzQpmrqwlrm7=TE7O&cuT%#9 zpcFx2^f4auUETQQL`8V0Dm;XY%JyT`?Z-;~+rz@rj`xQz4!=9{-biJ5syaON_tXFA zy!FJ-!aomx$X6bIvik6omEkALzJc4j_mpFg-kPh#j(sp%+5LER_v4VxXrh0p6siU9 zd+qS6htC~3e`N979|cFt!O^SE_Nk-%R2q+!eOeW`N2~79@JelHq%>2SsfBu8`|hjX zJ@?%C=N1<}3hge3c3&Os2(WLS!DD4CR|W3PRrlubiv6)YIEypoeUILX*9IS1DSqPQ z`yb~&K#|-VzNoFxJ(GZ=2dXrV*~Iz6#up;be;9l zCZN;OqPIgnc{f?^YquPd-uDHP-oI5rUksq8z(!5QU_;j5mD%4a>$?iBc0t(DpRUgS z+*O!|+WS}t-9wm%3l7LpR{dY6K%I^>HK7CAcLMB7G4N+pr9nW-o|yz$ zf&~J*^an`ALhL+c{1pJ_mryZvS;T%wxvx=fcan5g+i4p6F=dBaa>&+E?62+2Hq8TZ z#onM={+3F%HAg0=D*JXX!Q4e(Wy@ULp8%0#bggL~xGaF9EU0hTz+nYSSfJ3Z*cL+1jz6NT{j-z>M0)ohQyRE781BX~XdV0kJ7iAAc>4@A=B2}4{C9&K zk6VFwy#Akt(#hdniiqjI*~Y<819pZgBi`*Va{#w&CZ iVtfs-=Hz)E_V6y)0|=aS@M!Dq_=%nW={-tqL;eT#G5?-{yB3l|M_p{{NEh?DiRSmlmp&hXY4_a`wx0?i?3N(8$snYPUfaL z4|j}{`IDUNx#(&1PV+@?!dEH^lX@>r6Cf6>)M_ zQZ-Rei$|H1HpQvQr$s|y^9mam9?_(1v7{Jcp6N4LC1;4HF3O6ju$-zXqLfE_o~e?l zhzmNC#jKiBO=Db0x#H5ZDHOMMKVDwr6mFV_*dCdm_R5}<+_X>blD#MSX+NGmJiChi zM8NKC%l=qHbe)wfK}2c3Lo5X`(4a#@4iy|4c4)UlBX96> z^z{H{wpJ^~5*5zDC;@6ZMTtEQfgXFb((>}yTUPS=u#q8W-E$j=rHHD%bL)U1+I4U-k^z>KP6Y|-|m zbXl>x@`X|kxMbf)C& zU`ooS3fY2TcR>`Up0K;-m6Qn=N3)15iHTA+qs%bL4pZn!Ginw=Yzu}7eh7z@QR>Mk zdQQRI6osp*7jG%}Pesa(IzbQI@Xr5B8&n9Wfd(23u#KGVWF_B8gm7$u|ybL+?Pbu4lyNFUPg8C`B4tG34ftY@ zkh5}mcuBScm_s7kVe(E^onb0;@JT6EwtX`a+!@kOs4$V^1E$EZ5>*k$CXPtMw7XOT zN;0hEqisQ<0qIoDhIXZ5PQl($vRS(e;cFCNMV)0NbQ+k8{U9Kcr5Qa-K^4u<>ZWcK zHJHOt4Li&fBd<1X6`sW^I<^6Nu#pi18hnP6z_f!#Lt9!~G7)*2?RSm>HjG_wBc^iX zQ%CM_9+oc9W`Ys{CwQ<;g2W2@xR?kqI^bBCNDq++ktmTEk#!))6CQhg;}}YkiIR=e z-;THAGO1!e!9+SU61?p*lx*6db4~p3M<$-dt~DlR)uJ?0kmcfpp_+;@F@xQqyidy<4W#G|^@}4p#@0V6U3VWNBowl>=J2V%+WR#(Ks#L0;l+1i5%$9lw#$ zdjCLuaER!Jc)hP5m7Bf7W})J{796`Z;)_Rb3BEoePHg}+?@;ts(2H8o+aJC8@O}4V z@V^B&t${O_qEz4(I%r?U-`ZcJ^%eg2I35YY;^y4`j^|wGyk@)QnWIDdj&ia2F7s}Z zFMCZ|$th;Yt)1fM$Z2;JclO0*Qjt9+jFP=AUOw4Z_CrQ8)*a=ymodY&_;y)cN4e*= zb6h!K1wKzN$o)B5&vOQUdW+?k{Uroq*?)S6k85i$`_a=?!u%3smjkWUgR&odDPZp? za;z zWcIp@+cT}OD~s%RwIolLyW7^Vx@E!fZrB}Bj>z5R=sn^p@_l%T^_$SFQ7U{IP~>D# zjveJ(o!r%#SMDiCwZuJmu5)aG%e>gFODq!UFd<-UU99`{R zAp-4+IHVX-3-YQ1Od4R3SbbeVCe;j45|O`wv2e0sRvRdbID{R5*`C52(6$d+t`cls zC3RP6(|-KZvs8Y0_~U>1XDa`5*yiyYU!~~&n1*rOr-w6*%8kR$@%(9tG}FR|zGv_l6eDW1r)L!|?EJFJxk#8;9XNBqI84wPsd8gI>g4y=qZ zkWc@U`}8GlWv5nsI`@HZ>FDyYD^r)J-hS%Zu1U1qUL$2+9vR~kUI%#skU>pEH%O*o zzfTT3dF{(I5z}eSJP`(BbQ*-sqvii&3i`0>_3I}-*s>%qXRhQf=ib(@jX#E# z+iU>?VTO=OAe0~u8%GP<36k0Gl(oaFB-$!v{6se+Bir5#ko5~r4ZzO!0#Vt1BM%VH zzDKQo2i&3w@GNh8bJ7XhD``al4uVsOb&TX=1sdAbfC#qG#t3$#fzXl-SZ>E!FaiDt zXtsq`1HMCAb-SlkZOsI}L+Ie^rOArN1boN<7Xs7U>uIwYN$fNFx^WuqA-C2MYwqkw z6V5c5MPM}SY_~Tm$#Vt3HMxm&=JQsK?az65AtTwV|d0%zp z!Q~ff0}oXqH@djtkxHx{AN(jjQH@W06yINs?_YlW%2zLc_3h*DJX(t%s|ddy++2C0 z9ve9G;;9#FG4b57x2Dcdz4_FKvHkU)v2%N?JzHLl-SBfmTRsVLBV&s%oO^0HRo(JX zb?m^^$=dLdmEeH0%;Lh&%V*0=&(y~DE>Bj+ey2M0yOkJv?%PhHEv2rB`)V7GRK!aei{xlO|pa0DWx(`U;i<9f+WX z{mP-Xp0dvh0bPQpgCVO`U(yJ|G_B7My zRT;OAbOR}=SzsxN?kDjjOt+VGPlOEDc@fq)#+%o7bV%cyGhN6!H=nXHQ^;iKijVGs zkhJhMihB{6TG}AQy*9;tT1yTGqcIt3F@*?$E;dUh_FAV z`YRx~t3Y;YWIR@()>G8llcp5f@#V};Q**SH>6{eF{?uvg$Yscz?8n6ARqEN`CLmhC zy#!8#o$SuaG>o!4dy&XXM1DYoay$DWkv}BzGLb(5=}hQt1=wrU?@0b9sM%hmXSPUf zq=+LnJ4-c^mHio!GelZa8cqi7%$`(lWYC!7+me!O`n9zAZrSsWL1b~`JTfP~^@V$8 z&A!3K{-2MY9sSu@EuN?d*9V3dpIlmh_Nk?>)dnV<%t?9oGn3}O?|iT}{Ot<|&P`tJ z{infy82rWX2Z4W${4(;Z*oO~&>!*`T2apz5hQED%Xmn-ccx`B+61zUU@iTJg(0wGx zIiohXvl6Wj4KMy+*;^gkUmbejizm?f=(bCnFQt|f7qzQWZO4)7C=&B$WX=B3`p_2o zjqa$Ajn~CJ^xL=#_3In&JD2#GR^RkMeaF%3TedCnm-b&OEKk3``JLR#v&u?l{szYt z`DZ-1_?YCs;kn6iU-<^T?0MFMo^N@+Lod6giEjOU&&}azk8p#FhJ@RqJZ#NI>)LXI=&8{>wT=Xo&Zeg7pK_wd3k Jj;J&5e*u9L^mzaP literal 0 HcmV?d00001 diff --git a/__pycache__/examples.cpython-311.pyc b/__pycache__/examples.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5dc0268656932048a5b3c9afdea91bdb35bfd64b GIT binary patch literal 11766 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 literal 0 HcmV?d00001 diff --git a/__pycache__/examples.cpython-312.pyc b/__pycache__/examples.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0db9a1f1f0f8c02ebcc15bb75c73e2248a62c6b6 GIT binary patch literal 10178 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 literal 0 HcmV?d00001 diff --git a/__pycache__/format.cpython-311.pyc b/__pycache__/format.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43a69a524782f4513a4548710ee569fe22d1626c GIT binary patch literal 7172 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 literal 0 HcmV?d00001 diff --git a/__pycache__/format.cpython-312.pyc b/__pycache__/format.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3786e4cae79ff1f4ba2c5f56c5640339c7238193 GIT binary patch literal 6229 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 literal 0 HcmV?d00001 diff --git a/__pycache__/reasoning.cpython-311.pyc b/__pycache__/reasoning.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e027d9548f38c10ef13b6e456bdd00245ab89173 GIT binary patch literal 7377 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 literal 0 HcmV?d00001 diff --git a/__pycache__/reasoning.cpython-312.pyc b/__pycache__/reasoning.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7cc14c7a6f4ac66602d931310cf5296dc9d3b88 GIT binary patch literal 6933 zcmd5>O>h*)6`t9d{nhGs5lD)V#vrU+5W5C!i^%*~NPul58IUkG1}~$XmUduwXPKEr z+GRz7O~rDBs^nzk*rc3Opj@f=$T4=RQdA|Wnt@759{WHlmr%|@L{7!wl=pgOfAnLM zQ_54?p6==H*WIsQfA8z3Ki1X;7R^_vnety>-mG4E~iBpH(}4zPMv8pOHnA_UAO@ey**C7XGJ~g z0{i(s@_{_IBrore1PZ+`XJlO#Q*wa_Q?j0f`HHDLojGo*oUr%3YTAM%>SFYv7sO2N zqR-d+BQ9%QqP&A~J*g5csiY*UNlYYippSgz+e07r{PUmi`N#f;9xVOh1AHFtkN7Me z_C9Ix2}L57H=E3BphU|L){+J#s(H&hE-NWzGH+w;!CuRm?aIV<2~R?-Y%kGl-txucGN?8lFSM;PzG$hg4o1^1L2`w8(7JV& zzh1W(WFlJ^p5FS%%{A98252EtUBH@_QVkZ`ste^B?6bbQWI{T6x1Uah3YM!$_`6Fst`nH@Kg;-OLPEb8r68eOr4hY(1A&h6aN5He(Bhcg8BM?0iwV@Nm<5LA3T0&6?iXq$aYfcAjtyBxN zS_msyT~+|5mWwG*=s3}HYG%bc#&U3C(P@QCSl;8{djwUclCoP8Ktr2AZJQA1Av3nA zELjb~#*U~6U={~8g>|I`S4}cpXpRVEIzfnpe#JpTA#AacL>YTQ0v?@R@LPHwlF}hN z)5tJ0Y!Q4L8imB34})2Fq3pu)`|J&tWv+*KCeKk5;4NQiIXIy~C^~pMa+?8|b48!A z*sKF;Y72<4{Cg%zhD>MGeT8RN1C#CZ<0YZdJt+membNci?sXi5XfP}0@65MXm_>%& z_`Lb-p<5RgIyyfNe2~7CyVvpTBFA)e&J}K+ftvqlh%9e!Xk}GAx`mFHD$cMpS=;lV zsfG(&bmv4N6anUtQl_f)7jdZq3J4F{K#vwqsR#nFO+}EQkckO7Ap_3mBR;hmI#}E! z(a~E>5miN{TOndo3=*$wo4O8bTA^vY%Gbrm^KsjjEgqDngUI#R&g+~6%j48GR3ycz zm0E6eIN-UQu@u#6vfEb6w}Nms;w3d`Xjph6@UeHRe#M3qo_gY*Ap_JxU}^RSwK0v& z^9^CMAw1vEV>a~MdhNHTZ=e3;tvh4$drz2qPZ+Po&An&uH;A+DMGq5fH)=M2)w1n= z%l6rq7q&#_w;V9H9Qa-D-9z`cjLvtC8o_qjYK|CPeP+iC_iA2P2(=jP&lo%Ujb{!R z2TmHVpE2Ij&DV3r$&1FtDWh%L2u&NoY3SA!{`J0J?VI0x#N2!Y1a2Ok-#BXc+P>QG zqeqSzE#Z5i_k|^nZP_~;#GzVXLbrmS=8V0^%--Yo!pG@&eRuZU<&3@|V<>K%CB{U- zJbTHA7mebK(RSGgT{eQ3p>sn&yV%JHW9+w!bxg+?`&jD&<-FbBIuKw#`w=_f;XiBN z4f)U4u>(H-^X8q9|GkfebeTIuqruxJuqU? zb+xJJ`mPofCpg-UyBY##x2B^mQU@#X*e>tNyCUvF}L2v3iM+5?g)GSVjW&{xd3DM!buCSirH)t0=v)tewyy8sCg zq?*RLb@Qz|O!%wWu@KueA3JQu4&U{gvE%cxlVzr{r7?g0KS4-)ixM)897H*L(L!y88ibmvbM6< z+GwQmYZ=XQ=0h0>NMHGk0+<)}9wxc~Y#J!2zIxYEDQCMh^jqJtiO$ZcPs0F^M=YZo zx6DJ-QxL3J#afuNqR9jZ($J4xVg6dvxDeVfAKGSyw#|oPW+?Wl=MF!=`>475sB!F+ zx%h2^g?}TzP{V6?>2nhRM)pzpzb3ha0LMBoDtI`9=8JVxPAGAd@vrrkP}l* zjW-U!oQTI&w10IcCeLBgi^(QTaQ`TXs{7M~;k> zF94gI;WIm)(8FEs;c>rGgVm z203bpfI8F@d~{mLY}ZIe13tQ(h^EEq_>`hb8oKAWrju-+Ff2~X>0DZ%-|^;N6%oJ| z_(rcAIi1npwqF2jv7Mw`$%dMOq$4O4BF; z(0LUpIA~9!oeRIDuQ!yOAR>Xyz#uS71P417LG`40Wl~glaMtMRq^&#*iHLoHIf6^fcpr7Be#^vi0Q!Bj1Ey02z=3X=iuJ2MJ?uWAaK;)i9SKy24Wu9 z1V)uq4n_0@EPr_m9(w|~(17K!6=AV~$NqBH+b+vvn}Nln4cuncZV(1s6lGHUwwhMk zujMP5oKy)e-6z;THgNimL=z#{#EIEzTqcDCNe%jZNz-6F~ zYYhPH0@v!_xX8FV+>2g!v+v6AVguu=d$)MC_|E0I_4mA;3%+Kfb=dR`zgN59um4&5 zd+k5%n0x-7|0!BAX8OjeD}H>>-$g4%Oy9_CEex=3{gt6Fy!Bsh=$t*g5Nw${f2;e` zh`Du-5!rj^%-vpN^eyv9+<4`zaW-klS>r<9lnX}kl5uIqXuoU*FTXbgF1|$oNzEN| zlXGtvP2E?HKq(}A@m&AUgLAssu=(TjX2T8$l>|MD4CnSdIDEWPu(<(xg3?p=3_-^; zK-}T`J%Y=@cTVCc)^bnu2zvc4l!TwR4(ucF2^IFxA3D^aFD+S z@zMZ zjsZX5YIzx+UL_|o8Se?G=F=1VI} z&3%Jh8MuwdVdj@=6sFWt&$+Q!ndT8fn-$j01lJYj77M<+Bn@|%PV`Sd3a#LUumTI)%q*3SC+BIz4|kcb9%D5+DH*AOR8}0TLhq5+DH*AOR8}fjt)Z2C|fh Aq5uE@ literal 0 HcmV?d00001 diff --git a/cod_examples.db b/cod_examples.db new file mode 100644 index 0000000000000000000000000000000000000000..9452efa97f1a7e6c0dd77fa630986d324390cd45 GIT binary patch literal 8192 zcmeHMUvJws5SOzyL4+0C5DdjI1h`??76Y;JugQu4LFT1ilgx25M{U=qftF~Ch%5>s z71w+7{sem-`n-?S53x_OJ4$lw*yx^y>_LKKN+j>d``z#E#Q5TerqCR^GU+o7JJyF* zrDA;pU|H5e{)dgZD)@abzo&+t|Lt0^oS)+dIIHr(eG5P3RU%L#P$EzwP$EzwP$Ezw zP$EzwP$EzwP$F=52#j7=mL9IIR(?IwtP^tnn)Tz5tD;>vYB%a`1Kj$z%?1>`ux2j{ zA5L0s<7uM}t!FN@o;RE8U@ymsL^XYI8$V*_#6ZHCl97mdvqLOW1D?Q(di&_OKCRA| zeI}y0LoCL%m>0|)>~qb2@|k9Edhx798#`~GoYmW};QPiaSQCELuF~0jaq%zff%|@B zHW&#s)NT8V4lZAo-oZ7bQ9T!P0$kd zHlZH}fJJf_6!&E0D+gW%_#bVdKxlO%CS*j@aQ22eWN-j)-&}3HeWPu7RTOpz{MF|5#1WkaC7XPY=RbjT#;RJ#7Ip*X+LtH zUI1?pRAxzX1IR9T0gHO(ZoK$F=l~L~V(w}35UF6` z&)SS>RoE~J?L7xB(vH$Xr@Byk#ln=US*GnNczI?q`e z&U9X+ibdc{41q{$Gi(6O^qkx%MWTo>94~x${P*>rr53oDuCov~YjJ7b{6_SdA zaANEj$NCI~#(m)95iOhE6O>M6ruH!R@cz`=cH5Y5Qoy8U`zh^*4cL(8*S59~9^Wi6 zaf?5p1{e;%S1V-YAGJHTGUtCFBI19>bW>4gD#--n3v_S!g*adDYCNMNl9X(XX9_^q zbCjtX1?-k%Ont#DA|fATsv(&5Q|=?e`9|e{L&UjHJst}^EEZUAD0F?%?IJTHk3)y+ z0mp(x-9-tE(tZc`iIwSw2n^SzIT^!g|Jf`PRai$oOsR@%%+J6;Q8D14(hlVgO(id{ z!TXRSm5Wa=QiCyr=8hjgv*0w1lP`E3lGbc+`mL?~tZKi0=zz Q78yCQ@k#N=GhB850hl0#W&i*H literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..790a5fc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.8" +services: + app: + build: . + ports: + - "3000:3000" + environment: + - COD_DB_URL=postgresql://postgres:postgres@db:5432/cod_db + depends_on: + - db + db: + image: postgres:15-alpine + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=cod_db + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..3f36b61 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,68 @@ +import eslint from '@typescript-eslint/eslint-plugin'; +import tsParser from '@typescript-eslint/parser'; +import prettierPlugin from 'eslint-plugin-prettier'; +import prettier from 'eslint-config-prettier'; +import globals from 'globals'; + +const config = [ + { + // Global configuration + ignores: ['dist/**', 'node_modules/**', 'coverage/**'], + }, + { + // TypeScript files configuration + files: ['**/*.ts'], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 'latest' as const, + sourceType: 'module' as const, + }, + globals: { + ...globals.node, + ...globals.es2021, + }, + }, + plugins: { + '@typescript-eslint': eslint, + prettier: prettierPlugin, + }, + rules: { + // Prettier + 'prettier/prettier': 'error', + + // TypeScript + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], + + // General + 'no-console': ['warn', { allow: ['warn', 'error'] }], + + // Include recommended rules + ...eslint.configs.recommended.rules, + ...prettier.rules, + }, + }, + { + // JavaScript files configuration (for config files) + files: ['**/*.js', '**/*.cjs', '**/*.mjs'], + languageOptions: { + ecmaVersion: 'latest' as const, + sourceType: 'module' as const, + globals: { + ...globals.node, + ...globals.es2021, + }, + }, + plugins: { + prettier: prettierPlugin, + }, + rules: { + 'prettier/prettier': 'error', + ...prettier.rules, + }, + }, +] as const; + +export default config; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..343557e --- /dev/null +++ b/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/__tests__/**/*.test.ts'], + moduleFileExtensions: ['ts', 'js'], + transform: { '^.+\.ts$': 'ts-jest' }, + globals: { 'ts-jest': { tsconfig: 'tsconfig.json' } }, +}; diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..6a55845 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1 @@ +datasource db { provider = "postgresql" url = env("COD_DB_URL") } generator client { provider = "prisma-client-js" } model InferenceRecord { id Int @id @default(autoincrement()) timestamp DateTime @default(now()) problemId String problemText String domain String approach String wordLimit Int tokensUsed Int executionTimeMs Float reasoningSteps String answer String expectedAnswer String? isCorrect Int? metaData Json? } model Example { id Int @id @default(autoincrement()) problem String @db.Text reasoning String @db.Text answer String domain String approach String metaData Json? } diff --git a/src/typescript/__tests__/client.test.ts b/src/typescript/__tests__/client.test.ts new file mode 100644 index 0000000..e636e57 --- /dev/null +++ b/src/typescript/__tests__/client.test.ts @@ -0,0 +1,49 @@ +import { ChainOfDraftClient } from '../client'; +import { AnalyticsService } from '../analytics'; +import { ComplexityEstimator } from '../complexity'; +describe('ChainOfDraftClient', () => { + let client: ChainOfDraftClient; + beforeEach(() => { + client = new ChainOfDraftClient(); + }); + test('should solve a simple math problem', async () => { + const result = await client.completions('test-model', 'What is 2+2?', { domain: 'math' }); + expect(result.choices[0].text).toBeDefined(); + }); + test('should handle chat completions', async () => { + const result = await client.chat('test-model', [{ role: 'user', content: 'What is 2+2?' }], { + domain: 'math', + }); + expect(result.choices[0].message.content).toBeDefined(); + }); +}); +describe('ComplexityEstimator', () => { + let estimator: ComplexityEstimator; + beforeEach(() => { + estimator = new ComplexityEstimator(); + }); + test('should estimate problem complexity', async () => { + const complexity = await estimator.estimateComplexity('What is 2+2?', 'math'); + expect(complexity).toBeGreaterThanOrEqual(3); + expect(complexity).toBeLessThanOrEqual(10); + }); +}); +describe('AnalyticsService', () => { + let analytics: AnalyticsService; + beforeEach(() => { + analytics = new AnalyticsService('postgresql://test:test@localhost:5432/test_db'); + }); + test('should record inference', async () => { + const id = await analytics.recordInference( + 'What is 2+2?', + 'math', + 'CoD', + 5, + 100, + 500, + 'Step 1: Add numbers', + '4' + ); + expect(id).toBeDefined(); + }); +}); diff --git a/src/typescript/analytics.ts b/src/typescript/analytics.ts new file mode 100644 index 0000000..611cd14 --- /dev/null +++ b/src/typescript/analytics.ts @@ -0,0 +1,157 @@ +import { PrismaClient } from '@prisma/client'; +// interface InferenceRecord { +// id: number; +// timestamp: Date; +// problemId: string; +// problemText: string; +// domain: string; +// approach: string; +// wordLimit: number; +// tokensUsed: number; +// executionTimeMs: number; +// reasoningSteps: string; +// answer: string; +// expectedAnswer?: string; +// isCorrect?: number; +// metaData?: any; +// } +interface PerformanceStats { + domain: string; + approach: string; + avgTokens: number; + avgTimeMs: number; + accuracy: number | null; + count: number; +} +interface TokenReductionStats { + domain: string; + codAvgTokens: number; + cotAvgTokens: number; + reductionPercentage: number; +} +interface AccuracyComparison { + domain: string; + codAccuracy: number | null; + cotAccuracy: number | null; + accuracyDifference: number | null; +} +export class AnalyticsService { + private prisma: PrismaClient; + constructor(dbUrl?: string) { + this.prisma = new PrismaClient({ + datasources: { + db: { url: dbUrl || process.env.COD_DB_URL || 'postgresql://localhost:5432/cod_analytics' }, + }, + }); + } + async recordInference( + problem: string, + domain: string, + approach: string, + wordLimit: number, + tokensUsed: number, + executionTime: number, + reasoning: string, + answer: string, + expectedAnswer?: string, + metadata?: any + ): Promise { + const problemId = Math.abs( + problem.split('').reduce((acc, char) => { + return (acc * 31 + char.charCodeAt(0)) >>> 0; + }, 0) % + 10 ** 10 + ).toString(); + const record = await this.prisma.inferenceRecord.create({ + data: { + problemId, + problemText: problem, + domain, + approach, + wordLimit, + tokensUsed, + executionTimeMs: executionTime, + reasoningSteps: reasoning, + answer, + expectedAnswer, + isCorrect: expectedAnswer ? this.checkCorrectness(answer, expectedAnswer) : null, + metaData: metadata, + }, + }); + return record.id; + } + private checkCorrectness(answer: string, expectedAnswer: string): number | null { + if (!answer || !expectedAnswer) { + return null; + } + return answer.trim().toLowerCase() === expectedAnswer.trim().toLowerCase() ? 1 : 0; + } + async getPerformanceByDomain(domain?: string): Promise { + const records = await this.prisma.inferenceRecord.groupBy({ + by: ['domain', 'approach'], + _avg: { tokensUsed: true, executionTimeMs: true, isCorrect: true }, + _count: { id: true }, + where: domain ? { domain } : undefined, + }); + return records.map(r => ({ + domain: r.domain, + approach: r.approach, + avgTokens: r._avg.tokensUsed || 0, + avgTimeMs: r._avg.executionTimeMs || 0, + accuracy: r._avg.isCorrect, + count: r._count.id, + })); + } + async getTokenReductionStats(): Promise { + const domains = await this.prisma.inferenceRecord.findMany({ + distinct: ['domain'], + select: { domain: true }, + }); + const results: TokenReductionStats[] = []; + for (const { domain } of domains) { + const [codAvg, cotAvg] = await Promise.all([ + this.prisma.inferenceRecord.aggregate({ + where: { domain, approach: 'CoD' }, + _avg: { tokensUsed: true }, + }), + this.prisma.inferenceRecord.aggregate({ + where: { domain, approach: 'CoT' }, + _avg: { tokensUsed: true }, + }), + ]); + const codAvgTokens = codAvg._avg.tokensUsed || 0; + const cotAvgTokens = cotAvg._avg.tokensUsed || 0; + const reductionPercentage = cotAvgTokens > 0 ? (1 - codAvgTokens / cotAvgTokens) * 100 : 0; + results.push({ domain, codAvgTokens, cotAvgTokens, reductionPercentage }); + } + return results; + } + async getAccuracyComparison(): Promise { + const domains = await this.prisma.inferenceRecord.findMany({ + distinct: ['domain'], + select: { domain: true }, + }); + const results: AccuracyComparison[] = []; + for (const { domain } of domains) { + const [codAccuracy, cotAccuracy] = await Promise.all([ + this.prisma.inferenceRecord.aggregate({ + where: { domain, approach: 'CoD', isCorrect: { not: null } }, + _avg: { isCorrect: true }, + }), + this.prisma.inferenceRecord.aggregate({ + where: { domain, approach: 'CoT', isCorrect: { not: null } }, + _avg: { isCorrect: true }, + }), + ]); + const codAcc = codAccuracy._avg.isCorrect; + const cotAcc = cotAccuracy._avg.isCorrect; + results.push({ + domain, + codAccuracy: codAcc, + cotAccuracy: cotAcc, + accuracyDifference: codAcc !== null && cotAcc !== null ? codAcc - cotAcc : null, + }); + } + return results; + } +} diff --git a/src/typescript/client.ts b/src/typescript/client.ts new file mode 100644 index 0000000..8b54d7f --- /dev/null +++ b/src/typescript/client.ts @@ -0,0 +1,294 @@ +import { Anthropic } from '@anthropic-ai/sdk'; +import { OpenAI } from 'openai'; +import MistralClient from '@mistralai/mistralai'; +import dotenv from 'dotenv'; +import { AnalyticsService } from './analytics'; +import { ComplexityEstimator } from './complexity'; +import { ExampleDatabase } from './examples'; +import { FormatEnforcer } from './format'; +import { ReasoningSelector } from './reasoning'; +import { createCodPrompt, createCotPrompt } from './prompts'; +import { logger } from '../utils/logger'; + +interface ChatMessage { + role: 'user' | 'assistant' | 'system'; + content: string; +} + +dotenv.config(); +interface ClientSettings { + maxWordsPerStep: number; + enforceFormat: boolean; + adaptiveWordLimit: boolean; + trackAnalytics: boolean; + maxTokens: number; + model?: string; +} +interface ChatResponse { + content: string; + usage: { inputTokens: number; outputTokens: number }; +} +class UnifiedLLMClient { + private provider: string; + private model: string; + private client: any; + constructor() { + this.provider = process.env.LLM_PROVIDER?.toLowerCase() || 'anthropic'; + this.model = process.env.LLM_MODEL || 'claude-3-sonnet-20240229'; + if (this.provider === 'anthropic') { + if (!process.env.ANTHROPIC_API_KEY) { + throw new Error('ANTHROPIC_API_KEY is required for Anthropic provider'); + } + this.client = new Anthropic({ + apiKey: process.env.ANTHROPIC_API_KEY, + baseURL: process.env.ANTHROPIC_BASE_URL, + }); + } else if (this.provider === 'openai') { + if (!process.env.OPENAI_API_KEY) { + throw new Error('OPENAI_API_KEY is required for OpenAI provider'); + } + this.client = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, + baseURL: process.env.OPENAI_BASE_URL, + }); + } else if (this.provider === 'mistral') { + if (!process.env.MISTRAL_API_KEY) { + throw new Error('MISTRAL_API_KEY is required for Mistral provider'); + } + this.client = new MistralClient(process.env.MISTRAL_API_KEY); + } else if (this.provider === 'ollama') { + this.client = { baseURL: process.env.OLLAMA_BASE_URL || 'http://localhost:11434' }; + } else { + throw new Error(`Unsupported LLM provider: ${this.provider}`); + } + } + async getAvailableModels(): Promise { + try { + if (this.provider === 'anthropic') { + const response = await this.client.models.list(); + return response.data.map((model: any) => model.id); + } else if (this.provider === 'openai') { + const response = await this.client.models.list(); + return response.data.map((model: any) => model.id); + } else if (this.provider === 'mistral') { + const response = await this.client.listModels(); + return response.data?.map((model: any) => model.id) || []; + } else if (this.provider === 'ollama') { + const response = await fetch(`${this.client.baseURL}/api/tags`); + const data = await response.json(); + return data.models.map((model: any) => model.name); + } + return []; + } catch (e) { + logger.error('Error fetching models:', e); + return []; + } + } + async chat( + messages: ChatMessage[], + model?: string, + maxTokens?: number, + temperature?: number + ): Promise { + model = model || this.model; + try { + if (this.provider === 'anthropic') { + const response = await this.client.messages.create({ + model, + messages: messages.map(msg => ({ role: msg.role, content: msg.content })), + max_tokens: maxTokens, + temperature, + }); + return { + content: response.content[0].text, + usage: { + inputTokens: response.usage.input_tokens, + outputTokens: response.usage.output_tokens, + }, + }; + } else if (this.provider === 'openai') { + const response = await this.client.chat.completions.create({ + model, + messages, + max_tokens: maxTokens, + temperature, + }); + return { + content: response.choices[0].message.content, + usage: { + inputTokens: response.usage.prompt_tokens, + outputTokens: response.usage.completion_tokens, + }, + }; + } else if (this.provider === 'mistral') { + const response = await this.client.chat({ + model, + messages: messages.map(msg => ({ role: msg.role, content: msg.content })), + max_tokens: maxTokens, + temperature, + }); + return { + content: response.choices[0].message.content, + usage: { + inputTokens: response.usage.prompt_tokens, + outputTokens: response.usage.completion_tokens, + }, + }; + } else if (this.provider === 'ollama') { + const response = await fetch(`${this.client.baseURL}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + messages, + options: { num_predict: maxTokens, temperature }, + }), + }); + const data = await response.json(); + return { content: data.message.content, usage: { inputTokens: 0, outputTokens: 0 } }; + } + throw new Error(`Unsupported provider: ${this.provider}`); + } catch (e) { + logger.error('Error in chat:', e); + throw e; + } + } +} +export class ChainOfDraftClient { + private llmClient: UnifiedLLMClient; + private analytics: AnalyticsService; + private complexityEstimator: ComplexityEstimator; + private exampleDb: ExampleDatabase; + private formatEnforcer: FormatEnforcer; + private reasoningSelector: ReasoningSelector; + private settings: ClientSettings; + constructor(apiKey?: string, baseUrl?: string, settings: Partial = {}) { + this.llmClient = new UnifiedLLMClient(); + this.analytics = new AnalyticsService(); + this.complexityEstimator = new ComplexityEstimator(); + this.exampleDb = new ExampleDatabase(); + this.formatEnforcer = new FormatEnforcer(); + this.reasoningSelector = new ReasoningSelector(this.analytics); + this.settings = { + maxWordsPerStep: 8, + enforceFormat: true, + adaptiveWordLimit: true, + trackAnalytics: true, + maxTokens: 200000, + ...settings, + }; + } + async completions(model?: string, prompt?: string, options: any = {}): Promise { + if (!prompt) { + throw new Error('Prompt is required'); + } + const problem = prompt; + const domain = options.domain || 'general'; + const result = await this.solveWithReasoning(problem, domain, { + model: model || this.settings.model, + ...options, + }); + return { + id: `cod-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, + object: 'completion', + created: Date.now(), + model: model || this.settings.model, + choices: [{ text: result.answer, index: 0, logprobs: null, finish_reason: 'stop' }], + usage: result.usage, + }; + } + async chat(model?: string, messages?: ChatMessage[], options: any = {}): Promise { + if (!messages || !messages.length) { + throw new Error('Messages are required'); + } + const lastMessage = messages[messages.length - 1]; + const problem = lastMessage.content; + const domain = options.domain || 'general'; + const result = await this.solveWithReasoning(problem, domain, { + model: model || this.settings.model, + ...options, + }); + + // Format messages for Mistral without using Message class + const formattedMessages = messages.map(msg => ({ + role: msg.role, + content: msg.content, + })); + + return { + id: `cod-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, + object: 'chat.completion', + created: Date.now(), + model: model || this.settings.model, + choices: [{ message: { role: 'assistant', content: result.answer }, finish_reason: 'stop' }], + usage: result.usage, + }; + } + async solveWithReasoning( + problem: string, + domain: string = 'general', + options: any = {} + ): Promise { + const startTime = Date.now(); + const [approach, reason] = await this.reasoningSelector.selectApproach(problem, domain); + const wordLimit = this.settings.adaptiveWordLimit + ? await this.complexityEstimator.estimateComplexity(problem, domain) + : this.settings.maxWordsPerStep; + const examples = await this.exampleDb.getExamples(domain, approach); + const prompt = + approach === 'CoD' + ? createCodPrompt(problem, domain, wordLimit, examples) + : createCotPrompt(problem, domain, examples); + + const messages: ChatMessage[] = [ + { role: 'system', content: prompt.system }, + { role: 'user', content: prompt.user } + ]; + + const response = await this.llmClient.chat( + messages, + options.model, + options.maxTokens, + options.temperature + ); + + let answer = response.content; + if (this.settings.enforceFormat && approach === 'CoD') { + answer = this.formatEnforcer.enforceWordLimit(answer, wordLimit); + } + const executionTime = Date.now() - startTime; + if (this.settings.trackAnalytics) { + await this.analytics.recordInference( + problem, + domain, + approach, + wordLimit, + response.usage.inputTokens + response.usage.outputTokens, + executionTime, + answer.split('####')[0].trim(), + answer.split('####')[1]?.trim() || answer, + options.expectedAnswer, + { reason, ...options.metadata } + ); + } + return { + answer: answer.split('####')[1]?.trim() || answer, + reasoning: answer.split('####')[0].trim(), + approach, + wordLimit, + usage: response.usage, + }; + } + async getPerformanceStats(domain?: string): Promise { + return this.analytics.getPerformanceByDomain(domain); + } + async getTokenReductionStats(): Promise { + return this.analytics.getTokenReductionStats(); + } + updateSettings(settings: Partial): void { + this.settings = { ...this.settings, ...settings }; + } + public async getAvailableModels(): Promise { + return this.llmClient.getAvailableModels(); + } +} diff --git a/src/typescript/complexity.ts b/src/typescript/complexity.ts new file mode 100644 index 0000000..f3c8ef9 --- /dev/null +++ b/src/typescript/complexity.ts @@ -0,0 +1,213 @@ +interface DomainBaseLimits { + [key: string]: number; +} +interface ComplexityIndicators { + [key: string]: string[]; +} +interface ProblemAnalysis { + domain: string; + baseLimit: number; + wordCount: number; + lengthFactor: number; + indicatorCount: number; + foundIndicators: string[]; + indicatorFactor: number; + questionCount: number; + questionFactor: number; + sentenceCount: number; + wordsPerSentence: number; + sentenceComplexityFactor: number; + estimatedComplexity: number; +} +export class ComplexityEstimator { + private domainBaseLimits: DomainBaseLimits = { + math: 6, + logic: 5, + common_sense: 4, + physics: 7, + chemistry: 6, + biology: 5, + code: 8, + puzzle: 5, + general: 5, + }; + private complexityIndicators: ComplexityIndicators = { + math: [ + 'integral', + 'derivative', + 'equation', + 'proof', + 'theorem', + 'calculus', + 'matrix', + 'vector', + 'linear algebra', + 'probability', + 'statistics', + 'geometric series', + 'differential', + 'polynomial', + 'factorial', + ], + logic: [ + 'if and only if', + 'necessary condition', + 'sufficient', + 'contradiction', + 'syllogism', + 'premise', + 'fallacy', + 'converse', + 'counterexample', + 'logical equivalence', + 'negation', + 'disjunction', + 'conjunction', + ], + code: [ + 'recursion', + 'algorithm', + 'complexity', + 'optimization', + 'function', + 'class', + 'object', + 'inheritance', + 'polymorphism', + 'data structure', + 'binary tree', + 'hash table', + 'graph', + 'dynamic programming', + ], + physics: [ + 'quantum', + 'relativity', + 'momentum', + 'force', + 'acceleration', + 'energy', + 'thermodynamics', + 'electric field', + 'magnetic field', + 'potential', + 'entropy', + 'wavelength', + 'frequency', + ], + chemistry: [ + 'reaction', + 'molecule', + 'compound', + 'element', + 'equilibrium', + 'acid', + 'base', + 'oxidation', + 'reduction', + 'catalyst', + 'isomer', + ], + biology: [ + 'gene', + 'protein', + 'enzyme', + 'cell', + 'tissue', + 'organ', + 'system', + 'metabolism', + 'photosynthesis', + 'respiration', + 'homeostasis', + ], + puzzle: [ + 'constraint', + 'sequence', + 'pattern', + 'rules', + 'probability', + 'combination', + 'permutation', + 'optimal', + 'strategy', + ], + }; + async estimateComplexity(problem: string, domain: string = 'general'): Promise { + const baseLimit = this.domainBaseLimits[domain.toLowerCase()] || 5; + const lengthFactor = Math.min(problem.split(' ').length / 50, 2); + let indicatorCount = 0; + const indicators = this.complexityIndicators[domain.toLowerCase()] || []; + for (const indicator of indicators) { + if (problem.toLowerCase().includes(indicator.toLowerCase())) { + indicatorCount++; + } + } + const indicatorFactor = Math.min(1 + indicatorCount * 0.2, 1.8); + const questionFactor = 1 + (problem.split('?').length - 1) * 0.2; + const sentences = problem.split('.').filter(s => s.trim()); + const wordsPerSentence = problem.split(' ').length / Math.max(sentences.length, 1); + const sentenceComplexityFactor = Math.min(wordsPerSentence / 15, 1.5); + let domainFactor = 1.0; + if ( + domain.toLowerCase() === 'math' && + ['prove', 'proof', 'theorem'].some(term => problem.toLowerCase().includes(term)) + ) { + domainFactor = 1.3; + } else if ( + domain.toLowerCase() === 'code' && + ['implement', 'function', 'algorithm'].some(term => problem.toLowerCase().includes(term)) + ) { + domainFactor = 1.2; + } + const impactFactor = Math.max( + lengthFactor, + indicatorFactor, + questionFactor, + sentenceComplexityFactor, + domainFactor + ); + const adjustedLimit = Math.round(baseLimit * impactFactor); + return Math.max(3, Math.min(adjustedLimit, 10)); + } + analyzeProblem(problem: string, domain: string = 'general'): ProblemAnalysis { + const baseLimit = this.domainBaseLimits[domain.toLowerCase()] || 5; + const wordCount = problem.split(' ').length; + const lengthFactor = Math.min(wordCount / 50, 2); + const indicators = this.complexityIndicators[domain.toLowerCase()] || []; + const foundIndicators = indicators.filter(ind => + problem.toLowerCase().includes(ind.toLowerCase()) + ); + const indicatorCount = foundIndicators.length; + const indicatorFactor = Math.min(1 + indicatorCount * 0.2, 1.8); + const questionCount = problem.split('?').length - 1; + const questionFactor = 1 + questionCount * 0.2; + const sentences = problem.split('.').filter(s => s.trim()); + const wordsPerSentence = wordCount / Math.max(sentences.length, 1); + const sentenceComplexityFactor = Math.min(wordsPerSentence / 15, 1.5); + return { + domain, + baseLimit, + wordCount, + lengthFactor, + indicatorCount, + foundIndicators, + indicatorFactor, + questionCount, + questionFactor, + sentenceCount: sentences.length, + wordsPerSentence, + sentenceComplexityFactor, + estimatedComplexity: Math.max( + 3, + Math.min( + Math.round( + baseLimit * + Math.max(lengthFactor, indicatorFactor, questionFactor, sentenceComplexityFactor) + ), + 10 + ) + ), + }; + } +} diff --git a/src/typescript/examples.ts b/src/typescript/examples.ts new file mode 100644 index 0000000..6bb93bb --- /dev/null +++ b/src/typescript/examples.ts @@ -0,0 +1,147 @@ +import { PrismaClient } from '@prisma/client'; +interface Example { + id: number; + problem: string; + reasoning: string; + answer: string; + domain: string; + approach: string; + metaData?: any; +} +interface ExampleCount { + domain: string; + approach: string; + count: number; +} +export class ExampleDatabase { + private prisma: PrismaClient; + constructor(dbPath?: string) { + this.prisma = new PrismaClient({ + datasources: { + db: { + url: dbPath || process.env.COD_EXAMPLES_DB || 'postgresql://localhost:5432/cod_examples', + }, + }, + }); + this.ensureExamplesExist(); + } + private async ensureExamplesExist(): Promise { + const count = await this.prisma.example.count(); + if (count === 0) { + await this.loadInitialExamples(); + } + } + private async loadInitialExamples(): Promise { + const examples = [ + { + problem: + 'Jason had 20 lollipops. He gave Denny some lollipops. Now Jason has 12 lollipops. How many lollipops did Jason give to Denny?', + reasoning: + "Let'\s think through this step by step:\n1. Initially, Jason had 20 lollipops.\n2. After giving some to Denny, Jason now has 12 lollipops.\n3. To find out how many lollipops Jason gave to Denny, we need to calculate the difference between the initial number of lollipops and the remaining number.\n4. We can set up a simple subtraction problem: Initial number of lollipops - Remaining number of lollipops = Lollipops given to Denny\n5. Putting in the numbers: 20 - 12 = Lollipops given to Denny\n6. Solving the subtraction: 20 - 12 = 8", + answer: '8 lollipops', + domain: 'math', + approach: 'CoT', + }, + { + problem: + 'Jason had 20 lollipops. He gave Denny some lollipops. Now Jason has 12 lollipops. How many lollipops did Jason give to Denny?', + reasoning: 'Initial: 20 lollipops\nRemaining: 12 lollipops\nGave away: 20-12=8 lollipops', + answer: '8 lollipops', + domain: 'math', + approach: 'CoD', + }, + { + problem: + 'A coin is heads up. John flips the coin. Mary flips the coin. Paul flips the coin. Susan does not flip the coin. Is the coin still heads up?', + reasoning: + "Let'\s track the state of the coin through each flip:\n1. Initially, the coin is heads up.\n2. John flips the coin, so it changes from heads to tails.\n3. Mary flips the coin, so it changes from tails to heads.\n4. Paul flips the coin, so it changes from heads to tails.\n5. Susan does not flip the coin, so it remains tails.\nTherefore, the coin is tails up, which means it is not still heads up.", + answer: 'No', + domain: 'logic', + approach: 'CoT', + }, + { + problem: + 'A coin is heads up. John flips the coin. Mary flips the coin. Paul flips the coin. Susan does not flip the coin. Is the coin still heads up?', + reasoning: 'H→J flips→T\nT→M flips→H\nH→P flips→T\nT→S no flip→T\nFinal: tails', + answer: 'No', + domain: 'logic', + approach: 'CoD', + }, + { + problem: + 'A car accelerates from 0 to 60 mph in 5 seconds. What is its acceleration in mph/s?', + reasoning: + "Let'\s solve this problem step by step:\n1. We know the initial velocity is 0 mph.\n2. The final velocity is 60 mph.\n3. The time taken is 5 seconds.\n4. Acceleration is the rate of change of velocity with respect to time.\n5. Using the formula: acceleration = (final velocity - initial velocity) / time\n6. Substituting the values: acceleration = (60 mph - 0 mph) / 5 seconds\n7. Simplifying: acceleration = 60 mph / 5 seconds = 12 mph/s", + answer: '12 mph/s', + domain: 'physics', + approach: 'CoT', + }, + { + problem: + 'A car accelerates from 0 to 60 mph in 5 seconds. What is its acceleration in mph/s?', + reasoning: 'a = Δv/Δt\na = (60-0)/5\na = 12 mph/s', + answer: '12 mph/s', + domain: 'physics', + approach: 'CoD', + }, + ]; + await this.prisma.example.createMany({ data: examples }); + } + async getExamples( + domain: string, + approach: string = 'CoD', + limit: number = 3 + ): Promise { + const examples = await this.prisma.example.findMany({ + where: { domain, approach }, + take: limit, + }); + return examples.map(ex => ({ + id: ex.id, + problem: ex.problem, + reasoning: ex.reasoning, + answer: ex.answer, + domain: ex.domain, + approach: ex.approach, + })); + } + async addExample( + problem: string, + reasoning: string, + answer: string, + domain: string, + approach: string = 'CoD', + metadata?: any + ): Promise { + const example = await this.prisma.example.create({ + data: { problem, reasoning, answer, domain, approach, metaData: metadata }, + }); + return example.id; + } + async transformCotToCod(cotExample: Example, maxWordsPerStep: number = 5): Promise { + const steps = this.extractReasoningSteps(cotExample.reasoning); + const codSteps = steps.map(step => this.summarizeStep(step, maxWordsPerStep)); + return { ...cotExample, reasoning: codSteps.join('\n'), approach: 'CoD' }; + } + private extractReasoningSteps(reasoning: string): string[] { + if (/\d+\./.test(reasoning)) { + return reasoning + .split(/\d+\./) + .filter(s => s.trim()) + .map(s => s.trim()); + } else { + return reasoning.split('\n').filter(s => s.trim()); + } + } + private summarizeStep(step: string, maxWords: number): string { + const words = step.split(/\s+/); + return words.length <= maxWords ? step : words.slice(0, maxWords).join(' '); + } + async getExampleCountByDomain(): Promise { + const results = await this.prisma.example.groupBy({ + by: ['domain', 'approach'], + _count: { id: true }, + }); + return results.map(r => ({ domain: r.domain, approach: r.approach, count: r._count.id })); + } +} diff --git a/src/typescript/format.ts b/src/typescript/format.ts new file mode 100644 index 0000000..01926cb --- /dev/null +++ b/src/typescript/format.ts @@ -0,0 +1,96 @@ +interface AdherenceMetrics { + totalSteps: number; + stepsWithinLimit: number; + averageWordsPerStep: number; + maxWordsInAnyStep: number; + adherenceRate: number; + stepCounts: number[]; +} +export class FormatEnforcer { + private stepPattern: RegExp; + constructor() { + this.stepPattern = new RegExp( + '(\d+\.\s*|Step\s+\d+:|\n-\s+|\n\*\s+|•\s+|^\s*-\s+|^\s*\*\s+)', + 'm' + ); + } + enforceWordLimit(reasoning: string, maxWordsPerStep: number): string { + const steps = this.splitIntoSteps(reasoning); + const enforcedSteps = steps.map(step => this.enforceStep(step, maxWordsPerStep)); + return enforcedSteps.join('\n'); + } + private splitIntoSteps(reasoning: string): string[] { + if (this.stepPattern.test(reasoning)) { + const parts: string[] = []; + let currentPart = ''; + const lines = reasoning.split('\n'); + for (const line of lines) { + if (this.stepPattern.test(line) || /^\s*\d+\./.test(line)) { + if (currentPart) { + parts.push(currentPart); + } + currentPart = line; + } else { + if (currentPart) { + currentPart += '\n' + line; + } else { + currentPart = line; + } + } + } + if (currentPart) { + parts.push(currentPart); + } + return parts.length ? parts : [reasoning]; + } else { + return reasoning + .split('\n') + .map(line => line.trim()) + .filter(Boolean); + } + } + private enforceStep(step: string, maxWords: number): string { + const words = step.split(/\s+/); + if (words.length <= maxWords) { + return step; + } + const match = step.match(/^(\d+\.\s*|Step\s+\d+:|\s*-\s+|\s*\*\s+|•\s+)/); + const marker = match ? match[0] : ''; + const content = marker ? step.slice(marker.length).trim() : step; + const contentWords = content.split(/\s+/); + const truncated = contentWords.slice(0, maxWords).join(' '); + return `${marker}${truncated}`; + } + analyzeAdherence(reasoning: string, maxWordsPerStep: number): AdherenceMetrics { + const steps = this.splitIntoSteps(reasoning); + const stepCounts = steps.map(step => { + const match = step.match(/^(\d+\.\s*|Step\s+\d+:|\s*-\s+|\s*\*\s+|•\s+)/); + const marker = match ? match[0] : ''; + const content = marker ? step.slice(marker.length).trim() : step; + return content.split(/\s+/).length; + }); + const totalSteps = steps.length; + const stepsWithinLimit = stepCounts.filter(count => count <= maxWordsPerStep).length; + const averageWordsPerStep = totalSteps ? stepCounts.reduce((a, b) => a + b, 0) / totalSteps : 0; + const maxWordsInAnyStep = stepCounts.length ? Math.max(...stepCounts) : 0; + const adherenceRate = totalSteps ? stepsWithinLimit / totalSteps : 1.0; + return { + totalSteps, + stepsWithinLimit, + averageWordsPerStep, + maxWordsInAnyStep, + adherenceRate, + stepCounts, + }; + } + formatToNumberedSteps(reasoning: string): string { + const steps = this.splitIntoSteps(reasoning); + return steps + .map((step, i) => { + const match = step.match(/^(\d+\.\s*|Step\s+\d+:|\s*-\s+|\s*\*\s+|•\s+)/); + const content = match ? step.slice(match[0].length).trim() : step.trim(); + return `${i + 1}. ${content}`; + }) + .join('\n'); + } +} diff --git a/src/typescript/index.ts b/src/typescript/index.ts new file mode 100644 index 0000000..d5469a3 --- /dev/null +++ b/src/typescript/index.ts @@ -0,0 +1,14 @@ +/** + * Chain of Draft (CoD) TypeScript Implementation + * Main entry point for the TypeScript port + */ + +export * from './analytics'; +export * from './client'; +export * from './complexity'; +export * from './examples'; +export * from './format'; +export * from './reasoning'; + +// Re-export server for direct usage +export { startServer } from '../server'; diff --git a/src/typescript/prompts.ts b/src/typescript/prompts.ts new file mode 100644 index 0000000..5a88b7c --- /dev/null +++ b/src/typescript/prompts.ts @@ -0,0 +1,65 @@ +interface Example { + problem: string; + reasoning: string; + answer: string; + domain: string; + approach: string; +} + +interface Prompt { + system: string; + user: string; +} + +export function createCodPrompt( + problem: string, + domain: string, + wordLimit: number, + examples: Example[] = [] +): Prompt { + const systemPrompt = `You are a Chain of Draft (CoD) reasoning assistant. Break down problems into clear, concise steps. +Each step should be no more than ${wordLimit} words. +Domain: ${domain}`; + + const userPrompt = `Problem: ${problem} + +${examples.length > 0 ? formatExamples(examples) : ''} +Please solve this step by step, keeping each step under ${wordLimit} words. +End with a clear final answer after "####".`; + + return { + system: systemPrompt, + user: userPrompt, + }; +} + +export function createCotPrompt( + problem: string, + domain: string, + examples: Example[] = [] +): Prompt { + const systemPrompt = `You are a Chain of Thought (CoT) reasoning assistant. Break down problems into clear steps. +Domain: ${domain}`; + + const userPrompt = `Problem: ${problem} + +${examples.length > 0 ? formatExamples(examples) : ''} +Let's solve this step by step. +End with a clear final answer after "####".`; + + return { + system: systemPrompt, + user: userPrompt, + }; +} + +function formatExamples(examples: Example[]): string { + return examples.map(ex => + `Example: +Problem: ${ex.problem} +Reasoning: +${ex.reasoning} +Answer: ${ex.answer} +---` + ).join('\n\n'); +} \ No newline at end of file diff --git a/src/typescript/reasoning.ts b/src/typescript/reasoning.ts new file mode 100644 index 0000000..8aa5481 --- /dev/null +++ b/src/typescript/reasoning.ts @@ -0,0 +1,121 @@ +import { ComplexityEstimator } from './complexity'; +import { AnalyticsService } from './analytics'; +interface DomainPreferences { + complexityThreshold: number; + accuracyThreshold: number; +} +interface Preferences { + [key: string]: DomainPreferences; +} +export class ReasoningSelector { + private analytics: AnalyticsService; + private defaultPreferences: Preferences = { + math: { complexityThreshold: 7, accuracyThreshold: 0.85 }, + code: { complexityThreshold: 8, accuracyThreshold: 0.9 }, + physics: { complexityThreshold: 7, accuracyThreshold: 0.85 }, + chemistry: { complexityThreshold: 7, accuracyThreshold: 0.85 }, + biology: { complexityThreshold: 6, accuracyThreshold: 0.85 }, + logic: { complexityThreshold: 6, accuracyThreshold: 0.9 }, + puzzle: { complexityThreshold: 7, accuracyThreshold: 0.85 }, + default: { complexityThreshold: 6, accuracyThreshold: 0.8 }, + }; + constructor(analyticsService: AnalyticsService) { + this.analytics = analyticsService; + } + async selectApproach( + problem: string, + domain: string, + complexityScore?: number + ): Promise<[string, string]> { + const prefs = this.defaultPreferences[domain.toLowerCase()] || this.defaultPreferences.default; + if (complexityScore === undefined) { + const estimator = new ComplexityEstimator(); + complexityScore = await estimator.estimateComplexity(problem, domain); + } + if (complexityScore > prefs.complexityThreshold) { + return [ + 'CoT', + `Problem complexity (${complexityScore}) exceeds threshold (${prefs.complexityThreshold})`, + ]; + } + const domainPerformance = await this.analytics.getPerformanceByDomain(domain); + const codAccuracy = domainPerformance.find(p => p.approach === 'CoD')?.accuracy || null; + if (codAccuracy !== null && codAccuracy < prefs.accuracyThreshold) { + return [ + 'CoT', + `Historical accuracy with CoD (${codAccuracy.toFixed(2)}) below threshold (${prefs.accuracyThreshold})`, + ]; + } + return ['CoD', 'Default to Chain-of-Draft for efficiency']; + } + updatePreferences( + domain: string, + complexityThreshold?: number, + accuracyThreshold?: number + ): void { + if (!(domain in this.defaultPreferences)) { + this.defaultPreferences[domain] = { ...this.defaultPreferences.default }; + } + if (complexityThreshold !== undefined) { + this.defaultPreferences[domain].complexityThreshold = complexityThreshold; + } + if (accuracyThreshold !== undefined) { + this.defaultPreferences[domain].accuracyThreshold = accuracyThreshold; + } + } + getPreferences(domain?: string): DomainPreferences | Preferences { + return domain + ? this.defaultPreferences[domain] || this.defaultPreferences.default + : this.defaultPreferences; + } +} +interface Example { + problem: string; + reasoning: string; + answer: string; +} +export function createCodPrompt( + problem: string, + domain: string, + maxWordsPerStep: number, + examples?: Example[] +): { system: string; user: string } { + let systemPrompt = `You are an expert problem solver using Chain of Draft reasoning. Think step by step, but only keep a minimum draft for each thinking step, with ${maxWordsPerStep} words at most per step. Return the answer at the end after "####".`; + if (domain.toLowerCase() === 'math') { + systemPrompt += '\nUse mathematical notation to keep steps concise.'; + } else if (domain.toLowerCase() === 'code') { + systemPrompt += '\nUse pseudocode or short code snippets when appropriate.'; + } else if (domain.toLowerCase() === 'physics') { + systemPrompt += '\nUse equations and physical quantities with units.'; + } + let exampleText = ''; + if (examples) { + for (const example of examples) { + exampleText += `\nProblem: ${example.problem}\nSolution:\n${example.reasoning}\n####\n${example.answer}\n`; + } + } + const userPrompt = `Problem: ${problem}`; + return { system: systemPrompt, user: exampleText ? `${exampleText}\n${userPrompt}` : userPrompt }; +} +export function createCotPrompt( + problem: string, + domain: string, + examples?: Example[] +): { system: string; user: string } { + let systemPrompt = `Think step by step to answer the following question. Return the answer at the end of the response after a separator ####.`; + if (domain.toLowerCase() === 'math') { + systemPrompt += '\nMake sure to show all mathematical operations clearly.'; + } else if (domain.toLowerCase() === 'code') { + systemPrompt += '\nBe detailed about algorithms and implementation steps.'; + } else if (domain.toLowerCase() === 'physics') { + systemPrompt += '\nExplain physical principles and equations in detail.'; + } + let exampleText = ''; + if (examples) { + for (const example of examples) { + exampleText += `\nProblem: ${example.problem}\nSolution:\n${example.reasoning}\n####\n${example.answer}\n`; + } + } + const userPrompt = `Problem: ${problem}`; + return { system: systemPrompt, user: exampleText ? `${exampleText}\n${userPrompt}` : userPrompt }; +} diff --git a/src/typescript/server.ts b/src/typescript/server.ts new file mode 100644 index 0000000..8a2427a --- /dev/null +++ b/src/typescript/server.ts @@ -0,0 +1,79 @@ +import express from 'express'; +import { ChainOfDraftClient } from './client'; +import { AnalyticsService } from './analytics'; +import dotenv from 'dotenv'; +import { logger } from '../utils/logger'; + +dotenv.config(); +const app = express(); +const port = process.env.PORT || 3000; +app.use(express.json()); +const client = new ChainOfDraftClient(); +const analytics = new AnalyticsService(); + +app.post('/v1/completions', async (req, res) => { + try { + const { model, prompt, ...options } = req.body; + const result = await client.completions(model, prompt, options); + res.json(result); + } catch (error) { + logger.error('Error in completions:', error); + res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); + } +}); + +app.post('/v1/chat/completions', async (req, res) => { + try { + const { model, messages, ...options } = req.body; + const result = await client.chat(model, messages, options); + res.json(result); + } catch (error) { + logger.error('Error in chat:', error); + res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); + } +}); + +app.get('/v1/models', async (req, res) => { + try { + const models = await client.getAvailableModels(); + res.json({ data: models.map(id => ({ id })) }); + } catch (error) { + logger.error('Error getting models:', error); + res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); + } +}); + +app.get('/v1/analytics/performance', async (req, res) => { + try { + const { domain } = req.query; + const stats = await analytics.getPerformanceByDomain(domain as string); + res.json({ data: stats }); + } catch (error) { + logger.error('Error getting performance stats:', error); + res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); + } +}); + +app.get('/v1/analytics/token-reduction', async (req, res) => { + try { + const stats = await analytics.getTokenReductionStats(); + res.json({ data: stats }); + } catch (error) { + logger.error('Error getting token reduction stats:', error); + res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); + } +}); + +app.get('/v1/analytics/accuracy', async (req, res) => { + try { + const stats = await analytics.getAccuracyComparison(); + res.json({ data: stats }); + } catch (error) { + logger.error('Error getting accuracy stats:', error); + res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); + } +}); + +app.listen(port, () => { + logger.success(`Server running at http://localhost:${port}`); +}); diff --git a/write_files.sh b/write_files.sh new file mode 100755 index 0000000..a9bf588 --- /dev/null +++ b/write_files.sh @@ -0,0 +1 @@ +#!/bin/bash