From 592a54de8e4a325baf3f8aa7f3b87c17860b2f9b Mon Sep 17 00:00:00 2001 From: shreysingla11 <62649026+shreysingla11@users.noreply.github.com> Date: Thu, 17 Oct 2024 01:35:48 +0530 Subject: [PATCH] Swekit refactor and integration testing (#657) Co-authored-by: angrybayblade --- .gitignore | 7 +- python/swe/agent/agent.py | 56 +- python/swe/agent/architecture.png | Bin 125639 -> 0 bytes python/swe/agent/benchmark.py | 501 +++++++++--------- python/swe/agent/prompts.py | 31 +- python/swe/agent/run.sh | 90 ---- python/swe/dockerfiles/generate.py | 15 +- .../swe/dockerfiles/templates/Dockerfile.swe | 4 +- python/swe/setup.py | 10 +- python/swe/swekit/benchmark/run_evaluation.py | 101 ++-- python/swe/swekit/benchmark/utils.py | 121 +---- python/swe/swekit/config/constants.py | 15 + python/swe/tests/test_benchmark.py | 117 ++++ python/swe/tests/test_cli.py | 169 ------ python/swe/tests/test_docker/Dockerfile | 64 +++ python/swe/tests/test_docker/requirements.txt | 65 +++ python/tox.ini | 17 +- 17 files changed, 607 insertions(+), 776 deletions(-) delete mode 100644 python/swe/agent/architecture.png delete mode 100644 python/swe/agent/run.sh create mode 100644 python/swe/tests/test_benchmark.py delete mode 100644 python/swe/tests/test_cli.py create mode 100644 python/swe/tests/test_docker/Dockerfile create mode 100644 python/swe/tests/test_docker/requirements.txt diff --git a/.gitignore b/.gitignore index 2c0e8990661..1d13099f358 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,9 @@ refactor_debug.ipynb python/dockerfiles/e2b-dev/composio python/dockerfiles/composio python/swe/dockerfiles/generated -python/swe/dockerfiles/logs +python/swe/dockerfiles/logs* python/swe/logs -indexed +python/swe/tests/logs_dir +python/swe/dockerfiles/indexed +python/swe/agentic +python/swe/dockerfiles/generated* diff --git a/python/swe/agent/agent.py b/python/swe/agent/agent.py index c22db656ca6..665fd32543c 100644 --- a/python/swe/agent/agent.py +++ b/python/swe/agent/agent.py @@ -33,17 +33,12 @@ def pop_thought_from_request(request: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: return request -def get_agent_graph(repo_name: str, workspace_id: str, test_command: str): +def get_agent_graph(repo_name: str, workspace_id: str): import random import string random_string = "".join(random.choices(string.digits, k=6)) - - def save_test_response(response: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: - with open(f"test_response_{random_string}.txt", "w") as f: - f.write(response["data"]["stdout"] + "\n" + response["data"]["stderr"]) - - return response + run_file = f"messages_{random_string}.txt" bedrock_client = BedrockChat( credentials_profile_name="default", @@ -57,11 +52,7 @@ def save_test_response(response: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: metadata={ App.CODE_ANALYSIS_TOOL: { "dir_to_index_path": f"/home/user/{repo_name}", - }, - App.SHELLTOOL: { - "project_path": f"/home/user/{repo_name}", - "test_command": test_command, - }, + } }, processors={ "pre": { @@ -74,9 +65,6 @@ def save_test_response(response: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: App.CODE_ANALYSIS_TOOL: add_thought_to_request, App.SHELLTOOL: add_thought_to_request, }, - "post": { - Action.SHELLTOOL_TEST_COMMAND: save_test_response, - }, }, ) composio_toolset.set_workspace_id(workspace_id) @@ -118,19 +106,10 @@ def save_test_response(response: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: ), ] - test_tools = [ - *composio_toolset.get_actions( - actions=[ - Action.SHELLTOOL_TEST_COMMAND, - ] - ) - ] - # Create two separate tool nodes code_analysis_tool_node = ToolNode(code_analysis_tools) file_tool_node = ToolNode(file_tools) swe_tool_node = ToolNode(swe_tools) - test_tool_node = ToolNode(test_tools) # Define AgentState class AgentState(TypedDict): @@ -181,6 +160,12 @@ def invoke_with_retry(agent, state): }, name=name, ) + with open(run_file, "w") as handle: + message_str = "" + for message in state["messages"]: + message_type = type(message).__name__ + message_str += f"{message_type}: {str(message.content)}\n" + handle.write(message_str) return {"messages": [result], "sender": name} return agent_node @@ -209,7 +194,7 @@ def create_agent(system_prompt, tools): code_analyzer_agent = create_agent(CODE_ANALYZER_PROMPT, code_analysis_tools) code_analyzer_node = create_agent_node(code_analyzer_agent, code_analyzer_name) - editing_agent = create_agent(EDITING_AGENT_PROMPT, file_tools + test_tools) + editing_agent = create_agent(EDITING_AGENT_PROMPT, file_tools) editing_node = create_agent_node(editing_agent, editor_name) # Update router function @@ -222,6 +207,7 @@ def router( "continue", "analyze_code", "edit_file", + "swe_tool", ]: messages = state["messages"] for message in reversed(messages): @@ -252,7 +238,6 @@ def router( workflow.add_node("code_edit_tool", file_tool_node) workflow.add_node("code_analysis_tool", code_analysis_tool_node) workflow.add_node("swe_tool", swe_tool_node) - workflow.add_node("test_tool", test_tool_node) # Add start and end workflow.add_edge(START, software_engineer_name) @@ -272,11 +257,6 @@ def router( lambda x: x["sender"], {software_engineer_name: software_engineer_name}, ) - workflow.add_conditional_edges( - "test_tool", - lambda x: x["sender"], - {editor_name: editor_name}, - ) # Update conditional edges for the coding agent workflow.add_conditional_edges( @@ -304,6 +284,8 @@ def code_analyzer_router(state): return "code_analysis_tool" if "ANALYSIS COMPLETE" in last_ai_message.content: return "done" + if "EDIT FILE" in last_ai_message.content: + return "edit_file" return "continue" # Add conditional edges for the code analyzer @@ -313,6 +295,7 @@ def code_analyzer_router(state): { "continue": code_analyzer_name, "done": software_engineer_name, + "edit_file": editor_name, "code_analysis_tool": "code_analysis_tool", }, ) @@ -327,13 +310,7 @@ def code_editor_router(state): last_ai_message = messages[-1] if last_ai_message.tool_calls: - tool_name = last_ai_message.tool_calls[0]["name"] - if tool_name in [x.name for x in file_tools]: - return "code_edit_tool" - else: - return "test_tool" - if "EDITING AND TESTING COMPLETED" in last_ai_message.content: - return "done" + return "code_edit_tool" if "EDITING COMPLETED" in last_ai_message.content: return "done" return "continue" @@ -345,9 +322,8 @@ def code_editor_router(state): "continue": editor_name, "done": software_engineer_name, "code_edit_tool": "code_edit_tool", - "test_tool": "test_tool", }, ) graph = workflow.compile() - return graph, composio_toolset, f"test_response_{random_string}.txt" + return graph, composio_toolset, run_file diff --git a/python/swe/agent/architecture.png b/python/swe/agent/architecture.png deleted file mode 100644 index 546749009bbb32a88e3a56190406b6a701b1027a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125639 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

vW|7_Qi|Ik=9~bH5#S zN@3`o`9Mks5>A7(e<|Sb4b=^$qoX4-X+%QC5rGIIp`gVihhACeo#(6u4plc`NSIew z#~((%`Mnsf8~kGeHSBL78Bv0I1}+UP@-Sx>P3Aey)iyN12;e$vfg79=YU}ElWW++u zM!$bovA5?4mVW&?5W^k50W1l}E}(=#!iMYT-5$F&8Fr|CfcA|W$RdDm5zP(bfc%8F zxB0`NlgP3*Hy5L#y1l`z9H>9k&4+j3LrKRuu*wc!91`pUg=?JKk2~|T8myjBd4BDs z5OU#OJRM#|R0V4xl$T0S(9J-H9m$b$HTveJHiWZU%t7LoFnAZ%!`14+%V

0-qEQ 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

!if?G_oteJND$Ynplh6H?J;gPn(snuJq zSGT&kY4N?<6yk@mX?Oy7vMmyzCP@jy(3NiG{!7AvcQWb5!D&5Vn5$0s7t55C76YSN z{QBej%%a+AGk7z!Gee^;=^@e{uOBo%oxfqgW2ICyEB5N3@oYkk?C!Ug-MiL)j4A^t z`L}ryTZIJtjqi4UmgU^TDUTFoy#Cm(ai_Eb)_>{C{7sCdAoW-zR?kK=fcl zMPAFOZKuT7zB6{Ylw6%Te{?-HY6XuqM~E)HvO?upVjMDU6`++3jUaq~b>BF%sdWAO zAvPZ>$G5U72PeYfJa4Y@+EtJuK0=a(`$Qf11nZ!YVxDR>%E7PlY0T)L zWha~c`4@GwLf6iV!_C*94kuoCAH7R{zTa>hA^CZ4iBdP|GmJ?(fgYqRR<-z}i;Aj4 zom&C>@3KJEZ*yvq6pS0+`F~I8#`{IlJ-e>h)pEK{$KJH435%=;xCTF5`(2bzdnz~i zZSQqC%$D{Tu^kTFbV<##-LN)HqWjcnG}z;g#!K8LO6S)9-4B5*;UC>UZ>ZB zQxsn}xC?lg^rrnEhg4nDOImu5G3cuI9o$bs z-gt8{q`&CB^L%T3@|DA(XzWe4n$w+dv8Tbd?Gqw+g>!_xUv4X<@HunU-TCw-e)wni zhnfZ3waG8&*EYNFy?uDIKl;o~KWZT*$2T9oe{2}s!uNi4g zX=)q{Y994}7fUKhOd*-E*S$ug4vK#C}EH2+sa2$f+FP zu2y&Z=*Fts(E_i#Dd|7pJ}#@hIK2h&EnHUMv7O`ilx@cxDvm{y8d7~nJK;B5#bS$a z#-jtr&;Kq)49%y1hRNnNweqW&B{E5&&W`GfORYXCzegz+xfI#+1m+u~Tquz234~FG z-1neF=?0_9cPNjcAOACY3MT+GEBd{18NjG&7JB zcwDfwFx%l^gh!EGfu)AhdQhYa{fUt`BjSQuXjEV-bgyO$Pc_MFX-Sl$O2boEn;xpx zv5DpvxW#NYTBTw;XsPQfua|tXOMb}4Kr6n!@-$)Ux^@Cx$CTuHd6`v4oZIiPX6}p(Vyg3dU`m>$Tv3{i#d0Z)P-A!)!(G$nyTBUv zbc#e(Os+|PA^PJs7J8ET!nNPBkJ|WJ2!HtmxDH`gyWJqVf|>4nadq>$y|; zKY6f5X6EMepUF13l;ay1ZtdvT5Ifu#P9zo(VV45r?8eJr0*~J5 zA_kEJ;cssZHjDzzNC#ZJch%#E6UmSgqjd|*5^b7a;Iye??Y2xz|JT9k0tW2dPIVN~ z2#{j?!STzOTn7d+YLJgS0xRxlKZom=-R`ch@-IOSk0HJ2w#FOmwA_^{>X(K+y7eX% z-`JnPVOlj}+Fd`N|HGjdU#eb%0RI8lDH`%+fA9`=Mr^OHwjcW=rn(!Vf5buWdWpvA z^iy#A)4nReM6B^Tp29pj@N$K7X z5}dD`@U=I1<@cEuab!Ox1zI9hw2E|Dk4L;0`mJWB=2OwVo2&7#kG9~g5l%Lj9W+u#bi2C=(9F7;H)#)D@RMYLxfaGhu{ z;PJHQ<@p8(s~o*X^F%_H#PSJOQ5U>>eMvZ9Fo^_K`8s8j;eFi>yU9WyQd}j_f!5DG z??DyN0^^e~0HGt@UZD!Yl~ZI6CHUAH+UJ>8Eox-2(Xvk86y z!{P)*Mub~HW}S|x4^lWyuSjC6K00@AobfaWA^ZDebsx#PC9_5Z-w?*m{#86}P#%zRz1`l(pcrw*=ezo2VWDFb^;tTEj4Ddy3pFct6g zFv7#Y>)0gP>*vI~pU4jLn9L`6fX1YF@vmsXyVbK>L$-Qqs&Q~{NeOZ5vLuTXKW=`$ zo0yCQt#(80DNTwM5}T#CA)uLubNKA_MU;v`K!M(m1|Bt$lF<#0^MK|{H?`iF-s)XK z+50{YDOYGgdG$0klC98JQ!g-L`l5KnL~SI`XFW+eE1sJHTlB%P-fL8eB^cp%R0L~_sn{S~{~ z8{EsQ5-qZ=i_E5=J6Wl#pt(aZI3M20di&KnU9nz=XHI0+j_$+##UhVk>? zSCQGuQ#}%Z>8xyj+%^T}ltax|5-?56byuvWr+iSIq;WDlVX&01(Z$rp3bS3`3|jT4 zlF7x__ZhY<~m zZ(mS{_hWbXh|KrWL=}2`7gcCZi}-ebcITn|`w7JS3|c1#XiYsV9o`NLZ5{4QfhCF# zef6bCYQMMUV!JG0CN@x(BJ5=*TD2Rv+xx=Z$3$4TI4T`)BibGFiJb2!rv8+?%CNPK zwhyYJmYC@2*wbt!7UXY{3d1vnS8!-{j%tfnTg90(?ZiH)Q*K>9oQ+)=hJsDK-S2#6AQ( z`(0wmJe42pW6J=VxSBgAT>Mszr^(2xfCT7PJtMv`6DZ%Kx@_mCj+?m+Ql9U99`b(f zxIb%BajochE}-HA$f=S13Am5ls?BY67R>7rHS74niHC)MSDN^9_uC6%Xdh8?seFzr zP@2E{0jLZ$Z1-MatuwQXY1!L1+-)*+ijTl^vdPzb)?h9vXVZ|Acw|zDPjqfH=|LpP zdyqmRSztMo^@y5L2H~=!Jq1%d0t+dzoF<;=3XiI% zO=hzA2!RzfY_Fl&LptdZtNBz<^^>PB{{bHk?M<`NK#XfJWBUj*y*Hf_F|#JaT<3Z| zhP>ow$RkPm6jVTOyw;;ETnqg%f<5qM?^Zbbvzm%Ig*gQlCc9?>#)Rb(%c)a*$hlSjuR)N^jj`s+Ip4 zYKZJXmizF*GGF#|j_p6#@T>uau?gM4UT1+Uxn?hK_{s3f|CrK&$p663-P$Jq(Z)ts zppHBgrLdd$&8ZRVK`H|{`C$k+4>$n}ASMC&f<_~x!8MJ4n?EYynBbbNTu3FQs)*O9 z`HaIVmV`KP-2g6xuKLC`4IPDK9 z#pN1Zhh1C)F|_dQ_+NYHnV$1Rs_4rVL|5aYpa0V$ftAu_Lb1RWeAJ#@i(-dK(g2uz zgs5CJ6)Qi?Ux$F)zI?SB{$+QcYE|z!VtpJ(8=IAAr||?B3y(RfV>wPKfJ_F+mSCaY z?V!GhdF!`GkA^UDkT=Ygj^81VXFYd(tK-N1r?BEo{`5R%#W${$x5qJ!$tLidNsQ#A z;2{_zpCcT9Kq6s|1Pueetcmko-;Br#`t?%}XulyL7PJKp#b2-8Tv72VpkAX|6TkUz z;+yYFV2^;}-Q;X)@)^awwTjG>Q)E@rc67;FiCV87jwo&*+!Cbz!F0MFndC`ToWy+_ zP2a$PoLGg@=*XX%78CF_Z~u=Xt=t;Vzv;g2Ns?jssG6-Thohc9J(LhQ0r?3ZD*F-h zd9?64^98IHjAt<5*@p}XL=Xa#R0Bu~6OUs=V`xB~rlb;)%fHE3KE2$wtKjkD)V`l+ zA?k7WAAo`pf+)Fe%%L#KRYA_uSJ-{n`(gJe+y1@uXIB7#VV^R&c2RAccURjJv-l8r zUa^}f!|u(qfSWa9tJ%TN55CF8b4_e++g{WD|I1TpdT699M+>Kai5vD^axgO}?)@kv z^q+{dPkp0EjuMtkU@8jgA~peh$gR zm<%E<+68Vu0wRYWGa3_=)oRkZb8l|)TT4A{Mx}sbrTqkvy`#3Z?a2l~qGBjA^6I!D z;!sdO?>^`&e6{I)&pj4Km}r!YPkqDUt!Gi{k91!XZBLcT9x=6?X_%B7t=|u5t}^J zL82J|0hY=H@*DZ+46>n(8;-nWVV^;iijZ(mV2*wF*suO%_PDhhQA_0~AV4J8=dLgu zW|tm2p9icJ*LgQEluQuWq7!F2>K*kViA&$F zvT?hXu4fam*6Q!@n+(}w?^U&32m^Oqxz=Fx!PqEPU2DL3YjmsqN}SVItV6K{pE)@E z{{S!L4=k1}_&xoSkZ+KT+X2rab_K763LM7nYnsTBe*@x2*lLf&V)-kPfdK;Zgm#WX ziS{~w>O1~fpEFVcxlgHQEcP~1bwZ^n(beeqvT-Zk2<+_miVh4n;KKcnr3(q$q+fW zo*>Wi*8=sWV7ubRy@$(beSfY0dou*o%Rm$H{Lv-$3Xe>T**#$ac#FMUVjM@$fGaAdSqp-B6 zic59M5a~Dw#2>;YN(JJELe-2uYq5PY_&mw@9Y_J3VqdH<%8gY@-&ki_I^-~To`DhaRvm=>lk`+p$N>98 z^J8*fG_Kw$_oRq@aY~h1&WLg0S)vn-e#_jfrkb%H&qcK_5wnu!s1k z9qK}8l;3_mWybv@%2BdA89j?I)`S9)E3^M40aHEm3RqxGtmeEhV9 zE|2>d?@aPza%@dSgzNwnKeQA%)YYDV!X8?^QGQ1o(T|PZ^Sh!f?tcsz zBv}OTQ9QI(sO~YAqQQsH0>h?8@E(#k6V!C=4?h|PPD*n9h2n6A3gYfY(y8EoeZd%1 z?GdWlHet}xh>+R-L%V+GMCNu9Zn9o4r3$w4v@0f`Jk{TUM2>*H_^dyW1Ct0X ziAoF3rpjqT7iHGVY|RGvO+e{?@{FCm!za+`UL*Y0`+clc3|oIjRf$x_3Ju~5>|dVU zKbSfh@@nmXi$D)oH$^<*iwdO$+tEKkvE55+bm`_z1 zCQ~oB@%`)nZ+%P6y=b;x-RD$6fJ1e#Ju~cBYJ@igGvT2ypRT3K6d$udYD~uo$o4bd z*ftq%^DIoKi?u*~4CmnsI8?z*vxIC1$l4>4L|P0lLr4`A6~Gmpi&C$04D<12nLB>M z&6KLu#B(4j&w}i>;Va$_Er$~!rt>5Y6a$?8P4U?oz|G!@&9d?K2){!TX-#ZZRx%J8{~cve={^F zws%Dl)o)J;A6~`KA5P=Mb1_Or=4>#`?MEPh3Tk>SYR!p{g=L6!jJnIUT2X6-%^Yqz zIwN(vz&ELR_FEFjd*8$4Yg?0j@Og_ijuRVdnDRGHcA(RXWGey0=#%k^M{_#ejIbK{ zXoLQP-zM_OnHCkl{#(o)Vd6+>F+OsA6qn-@cwjJKKXTa9vr?|291Y8H+Kv1F;)$7m#%YgeMv-Mf?(~6{%N@>?o2COJAHPxHj34U`2fI^SZ?SnF zjz2**zeYz;1I9C3)pb~C$@v(;#RAw(J@B>v|wAP zCBwtgmpFy@)&oFaoDNQnB%TiM!I3WRnEC))9Z`?#dq{W@?4ohJeY4r=!qKRaDzf#G zY-FklB^*>VuFwm>U?8fH%&Ar=m^*rmbe?7n-;<)ksxvpH{_x;opm>oCp${@YK}c0pZfvXdWgu0UnWf8)Y(XoiNEh-VY_BWJz^O+9&(xI!AGzJ zH)~1k&jrcGeG>-G_%WpFK9_hcL5Ouq}j{#`G_V5#I|=h=UK~4UE4Tk7cu@bZ*A}_iyCK{NJjZDY#?({DpiG zSCN+PAwPCdqW&K5d-wG<`Qa#}rwn-1XP^^7XJDCHYaTBj)SHu-T6L^cnEW8Q!mEdkw-!vPlnw27NyC-_?N(p2&Y!I~GPLg5M)J26>Emp|Lxdfv3ewArI9N*(D+*h7)E!D_C?pK3pMvhnBI|*ZJRU~2^F40vIIyNLuH0FjT zN}#mu!K8c@03HHPykHnny98eb5X)?W|53r}rDwE7!5!qn7-K3$^BTE&$WP7`+%8o1 z*5?^DFVgxDO865I+EWO{VcZaWTEML8ykTlkqKycgKyz|sJyvQaS?~BcmXFZ`uQ0y6 zCa;rRnb$~lf)tu?yLjx{)=-na&4^ciaEa&PE&y6|%?cgRV~BMu_QnhR70O@Ia=YC6 zuO^5w^`}Q+aUb|VxEruvI*~1h<%5ci)AZul@eEs8uuhTwq5mT;8pr8E%@R-_Bb-*?T$xWokI1AgKw4w$9=4aeg##cD z5lspbu-5wt8@Ayiux_J`-_b3qIVO?aRUvCOhJqrj*@MyrOBZg>2u`!mb}!1=&)huI zDNB0l`GmAX_QdGzCx*zS<`!8#rPf;J^v0FJy@xSaVN0t|Nqmd3%Wu06`YR|@=zwg!zeH;DinF$tMt`1R_irS zz%S(rr=43jr5D4_26m>Z*B0(|8#m{5kdT5H7s3k2l#c*ER3*i>M4*|96vuddz^2#7O=ZEJaPQD4c<5L^{b$qPK^z<{O<~bAIZj+ z46P4Vh0GNf=qFq`c$ctnh(1#{0n+HiLs(}rih#8H4F`%N&0}trx*XY>_fo6}aGB5} zNwWMlJ3CaYk%!$8v+(v&CK7)to-rc-_HkGy0ShC{ky8p^t#~Wky@0hUucU+v|8Tuv zHaG-c{Vvps;PHF5x;ugCH`w~TzZUu2;mNDcDiw`YaX`je132Ny@e4=>7bE`LJ!m^l z#djv7c^pPZy4>J816Mc!^|ziDSTT~sG75c1kapM(7p80Nz*_tElx>4*D4XuspVlXD z<>i8)HZW+t>Jn~FHzEYiHJ}PZp?LQG&9(UeK z7q=D%ZKz>>L-_ZZ-`#}W|My(e0+=l8B}5*$73^9T4hSaSc;}XCJbJzCLnrYnZwpW= zkZ23KR4qyxE{zT$LBkquEKAxF_eTtdw6)K`@QWKz&o#?Q=FnnV=;U9*Hvh;zasc6M z%YQf3My~mJCDRXws{M-J#u!jspMS#6GoHU#6cYvJWJ!=O*uu|g9Wkc6Y8U-~C*KRG z*Ku(UXFYk&U~k%9!)Nr#l)ToI5nF*+KY=z};DjD&0l}i6KxYfWYt%}_^11R%MS9e1&@{Pn~7Ok|=1%5YjEXFlOG46{6+I2j5O{W`&bHAjp*0C%%f#q~4GK zQu1;LoOYnzR=VLhowqIiGl2er?5ZJp8$~T$L7fDNAVpFahac;|S&IbP@$-Xf7;t?h zuu)N|dnnR3Bx`!Xm3n!@-SP9L*sj~q<@gSLP#-PRw9csualsSBmUjWd_DkK-6!lF! z^Ij{B4vx>cTK^qTf0X}N%3U-CFAC?!*3xz_SF3(7h!)il2A*`i6O=BLq~GA}eP}0X zsbFfzN&AAc`bW{xoV)paO!FwM>;HU?RoGIpl#+8|hfcdJizr)J#q=?8N!bI>59ZS; zFcC-4V-MY9EKFfyah&dFbPz? zpHFD~?3#Dp>s;h8c+uYwir2IK|E7(hOgjyw84}HuYlXAJdENHy?pHLPYAL__Fyi^D z-a$Ja`gC4UXtDY2cMsx;;&mhgxBB~9%UZJ9Xvng5m?2M58pawBv#p31x1|!g1P0q# zL;NdxKUn_4kh+v(tZa1oXL_!6sy>WbJr#V?e8z^rY7spX+{*fJ^bq@(_WpEq(*+nZ zgqQ{OTH&y<7wu|4zdr;W0>mM7NF1zs-^7i4Oz1>y(fL}sN8Jq`>=#gsu|~MszIaGg z8JVzyP>rN7KR)=+ht*1k`fYuT-^f72`O_hT z<%1i@8yD0-6 zHVh3~YwNbjAC9wL7hXgK=-8#Uc~b1u?yRM674@-*5tL4U)}C}pZ?rkTUuEDGfgUM0 zTJ!~#_*hn*SUyjAee!akL>67=7qwSlVA2YK)Rs@pKLQS-SdCZ(197{{#hyj1+XOuu z?`K~HhOfI!g!yf#^(XQr+kZdRup6Xm3l;SEaC(dKsf^e9IDChfam>rh&9lBV3olJP z@O?#ze=S?;n9D$Mg~^~7ytPwcV^sOK7XFFCh0H)=G^;4-T=_M z=-CsU7S-mRW(&a&OgUdM8}RzRk_f&-*R-x301eK5=_7)mQ4*gP+{Dv4{_htH@vGxw zM+N~DFZOGV64d-3*6a2!x>yZl6=iEbRKN=K<#n8rzbkvGn`$>smpOtj{EI5^ ztE>;}7Liw^-W!sueR!g13EOU%XAvUBe&AmcGML(G?QyOd;T^AYgnMh5*;n#S@@=lm zrILo79VLsARs7vtE^GU$7sR6c+eguA{sXDi^Il#bBmAf2u$bCApVN=ML%&L$cvv9h z&ZQ>Rc`zoaw|nfktEJa2BqXD^g(AQ39ZbKt9&{WPB|H3SM!l8up~g$gBOOn#PfBko zv1qPdo32=JsX0MC(JV{Z{mzq~;S=Dg#Kvs0^3+e5s29i8ZMD>0sQYIza>4!?=>F^! zE?mAnGg~x-rEtZUoxfr!r`EwDc-Arb8``M{QTx{jJyA_fA;II11|Pm17wvLhovwO{ z4pjiIH=49@OE%}5#RfAq+d<4zU%$${a4`M2HqRydEK!U{b5x)Y@A;eiRAGhU6bq{D zXIOty#oJGPEY;C7a3z`gTAoKN6SJY3Uyxh$`C|g8Tr&9L{08;sUk$q~Ekrk}Qrq7P zKu$9_=>7pm7|-(Tcx1X7AAtI{A*7`wA_)}2m|jNreKV9&#}>|IM`-p8au=q^blDs4 zk3A<(J)DhwYw@WFTNu-4M9AenWV!E&`Hb!FD2LG~VwuI}FBk?Az4yHHgqtL@yJ^hk zjM4|g7A!ufwnW4(;*+>?Os$TN6ZwNj*zA=hg~nC)Lk9m#oCO{x?#&_522SSle2g-c zD|;#;S*?Gwb$<5-{m3~`G>S}d`OXtUTt(u{H`sFFbJPuj=A)IWxRr3oUEWfd4>}SU!Sj?Oa==H|_Iybkr?AT;Do)9U z^*q0pq5XtMT(&8B4!e(?U}~dfS4GEzKqmUcsWY7qOFVUCgXYm3$T4%zAEl@4IfBckQiX)aK>4KLg?gr!h@*;{2!w|DuOj z=g0Sg+V0C=n>kvd&VD%@ER+9{%#YG*vw+|5eD=9qV4A=3lm3F zD0=pZLs)~tGU9fIHADMqyCTttgVt$TWmQKX4#sVl4O>=`Buoo4 zqjZs`J0*9$D-!rj(55Zx39n6I=<#e9?V$96HxFqqhaVnNJ=%>hb1gm@Dw%NIyBjiN z!QJ6{yImS0ua|RZB429BobIxfvHJS8i1%m^`brVqC$>#x5y3kW^f#rFks8#z66#;R zd76+TTeR%OURrpP5~l|}Mm&+l60MHXIYX_poHYK?)}V`7S?{z$uG)!J^DSbji!*~15usNaH-cB%1V7zWZAJl`qC}m)9RUK;5u0lGRQZe+FI+9v)`}Qz^4vGpe z`BEuBix^zVQz-xvKdnFwbDy-v&htixi5&36yV;I6e+a%aaILnnepX{zsIw}I6Rxxz z@mN=9@7wcljyN`KVzfxeEC&XC+9dY@x z*DZxH7OO)9-_uT2wDgOaBe~m=k#QprIi>Qv)3`%4S)U#;m9N^0U=K8kioU!XG#LT* z7MCeq?BEJRe4kXf3Bx1gPkK}^7>wGtq33S+IKqMpo#qCR`G}=m=fgGgZ%Q*?*U-F0 z^pZ1xwC=&H+kaD%g!E_>_>DiznygMEQN)kw2$I1Eccfjy+c4X^Ih@vV8_Qx$U+WsI zq|zyL^Hsz=uU^OTq3I(U`Quy~j!UTbMJFt)O62F|`~%}Ey)W?Cl;XThMT&ARzIR~7 z_e?GQzK5M8Y2G2lh-3DNySx8h_1@yHd{O>ju-KLx z=2Ztd=9jbctV*YrYd?S|@N?$bz)4+fnJtbv?YlSD9!g0ur&?(=!F+NWks-^p+K!Gmoi)S1)jQ!Z*luqcFBjBMcQTvV1Zy(RE zDCVz^AC$%MtzR8cvY&Stz5kKw&tG3fsUkmm#j-)xpC5kgQU(@?jr~~WNxY^Zh?UWF z%i*{-g91(HRma^-_u@3%B`l=^7CHW7*7UQVzp=@Jl4wu#EJe|qf{hOKj}wu)b22@+ zkx;k8Fpr}kt)1fFSOA&pN4t0^{Qo1VK9KMAQ+b@p-7&;2Ts-%5enoHE!H00I^HtH< zm%u|M$-IBLDl-Bl7l%7b(hPk|Ki%XWXqi%f(Eimao@&W>y;@#A`cJ z!8{`AwL@$|Uy|s@Qa3Ym#pdQP7P@zJFV9#tEjiLY5=s!FuaR+w)jcs!5^BOND(Y3^ z#OCmyVztrUuSNYFFY&){A;je`_?svn>TGU4drXmizkKhAX0>=bX~@n-o8s?DsT9e^ zeX#h|)k%IDI`T@0*Op97FOcS2g^I+#n$oX!i|U=$H=zmxQ4PPB&On zPd16g|MrKa-UcF*S+@_aKnpaKdIASdP>ZpWhZz4>pnCRC2luF{;p4x_6W`T zertKu>9^nIR;0k;x1Kx%H`4elJj)#t5VMh_{OufI0B9q{x~DC1KLuO8mBCbXz4 z?X0Z6UYim<4Q#U6S6DPELdJEHzh?w&MAN%AZR)KsW&Y@5wV#z;pF_J@(!d?yw;Nx+ zIX7LNNh;s3zW6eqtYRVm^vA}L*#2emZ6)3p(w4OTzWKSB5va+Ch{&`O*cOg`&~@;73tl21+aS2E1SeA&^jOWes0e7m$6@Re-2Ie?8D$Iy_V zI~IQPNI{#W_}|g+ z+q32eS2B`R*HLGxTpn%RFEBs0yh`Dfq)y~}TtL4FddbBj!rRKXyC))z)c~mR-}ND~ zM-zWcdvjXBO(sj8fd_DYOqukGb4OpvjBd7n%~+O`Cb`tjdzmr8Uft$@aB+KBiQye% z?;VXP8DAV_u9-h8)j5_Xqa78k!Jo>t+F71!3f(f5rutNH0}$(;Np!3E&Iz2vVOI=h z-F3(BPE~oW-+;}J^0drLa+&rGJjSoLv+iI-vlj$V@c`YYQy>bBsK}JcmA#O z6HPT9wW_wx}L{{$QFQe}1(Nd1 zY3oy%O-i~E2iE&mv|Ji z`AoE%gKR#YfT1N`e)p;fnjZnT*tPNbuRFTqYRNetP47!JRysEP>CbhYL+Z(eYGm{h zVis!u3&G|-%J^DaU{)&N8-0nNwUn-CJ{QGqc?*@M3eS-Wu z>7et8cp@SZVAoPnF+m(Rd43Xfs@GQ&X@e)G7Wa|-Ik9+*23G#N=a^cm3yU#!4|^uS zc$9!P13NiX;F9xT3^(T5j~?~MsMf3VxIOn|%qlVUUx*UFNxG^>k1}*SYa(K!{l*q3 z5^G!#+9N+-f(aiYOoKzIs6td=XXaUS)qV0+f3z@a%ol1+Ge(QX2CMUf-yD>AFu$#C ziO<}d3pq^*-hcL8`0o{3PELDg!g-ALkgQYMm_9+TSL7HLnx689F0x$`n0GojT5Ss# zo#pLc*wx}NUK%ZU#-IyJ(o=H?$#Qt`mt35I*SPs|Q*{Em_}ZKQ6#`T@>9tq zFAA?Yk{mtqNy!Q!#6M&U{rn}@>86)td3d1gAp;XypGs07`q>qkm*3KR$kOlLlX7T^ zH2!IYxh%~z{b{+=pD~K{D*8m26mik%zL@6{x98_&DLZGQ$bc!yxF4l6%7xW!Dv!Re z$viFM_-eX;wNOn%Ocd`%Z^9SC+cz^M#VT8#izbOl@nQdL305jCYzkM9Zw?oxM>S{L zZ3XiFR=&YutoQerz(47I98}`ibh^rhIweC>Elw^mUZhNgpove>zZQCf3AMIDHO2Oy zj(JMtfn>=}dJ#2f`f*MpS6)cQF=+|^@-~RB5dB#u@wr&HkgmEeZC&s4m?&QF;sl%f ze2=>4H{73%;{cKUSHih9;*Pz~|E96Ki$nO%_dj}fadss>lTB*(xrY4qTYTo=rnhtc zUkMpRzG4u#W=;s^No@JD<3}2oMiRfhE?6|taTio~>sX0t5~Y=-jPD)TPbWS&f8)7M zt?1CZmgGRVTv0p5L{;uEo=bsa#N8E$E-Lbv(>ao@=`*NX99-+@KCNo0EJ?`fN}M#x zPHfjTeG+~9ck%9;vWM=bp|DQ|X*Q|t|CMtn5wi4ut&9JawIMvF4s%CI=G7`@`l)(J_vqETHE9h-YhBMz`Tn6BDD3yQ?MA%n*x(n z*5NNJ_V^mto}fSFmK*t%E`@Om^))p`h(Nn+e{#HJC}k2R_FrGR&jvk_(}(E7Wu4er zFxIL?DDtM`yl=nZoOLWcjqW5x<~5By19rQiz}wo0x#EGdGE1-Zx*i8RG8j|7Z~}r@5?&dSie<`Tb*041!e}aViQhg{62MOBOkvLmbENbq@rnFTS%EL zvm9acadEEX3CTl$z4p2h`&m{|wvzwq%~r-{je+S$UB(j8swqx&^ho+$Q1>+f57?T0U9Mp|tDPW%eN zY^OI0jOuSTPS$thu~7-o7@$k9-s0z&*B-ZKHM5+i#m;ha`%?QQ#FCz9jK7(euK+mF zbU`tZn)JlJdcKy)=0+)BcG+05Ef*hfhkZ0s-zMdvzia8~#5^%kC04oSu_Zj|`-_{_ z&%0x1$;cOP(XQ87k)GRWu8waWG*_4{yZlqrw=mJ-jA(BsaipgUXD=wwFXpKTfW2|$ zD6~8Elh%vp7I*LV#lP2AzuL!UtNkRhy2`R-cl&?qa`OcUW7L>s2z!_A$4cW&bG|iY zxPm7Jz7rGOCe_AjaejKlaLMZZyu>xXR~rnpcOo&cIwM}lRt%%pc#jej1Jq7`n=Q7* z^mFMiN;4ep^M}{K-iQKo%Z>hSFn5^Gx-?T?Q0Rlo61=O^#zk4)B%J)%?SZMhymc0D zh{Z`~sh(z5QsV4muAg{mp%^h~Ix<67oVmkf1+7talvS2`>c8q(!VG>n3xWwD)08NF zp_+m^s-(K=?w>zfsK9#RB9-?55~Y#l!WqAcE`uil|Y>cr`Z2VPp+_`c-qbI(FwY2 z-tTY2JMw2b0*boFrEai;#Lf`t|f1Z$L<)SfoU=mSB!M7@2< z+`Hyk3c0;&Oz0!*_lGmaX$|zLa+Xs2k*%(q|0$Bk?LbeJq-%Q21U%X5}z;!Vi$+I+#XQn-D} z%zXXv9j+dl;a6Y^(qcO{$fNT3@wHo@v<^%y9rOb%I6aVEtoK?JS$)(>i^`0(`*m#x z2WOpvl^h}@J<>f@OwXhklocj8B+``JKC|)i@tWCcvsF~ajs@=&g-n}i1?OdU)TR)I zA18$dP+<0~Y#dp~X6oe8zhH`gop-RYAw5~3qRszAnrcRUV5L(cJ9EwL?Hz%jnomd78N&(=}rgbpd$a6D6eap>Q((P3g<+gE^Rar z*=)uE4^OgIE4!e;AMaZjwRus=6YX7#B;I=TxF=6gr@{nYw_kvy{nd6{qj$NUL00)| z9+8UOfn6oXTM^vv$F85B#BTAY7Z)Rfi^?^+=6n9;f=!OgV4QLYiFj4}$z!QfCL@~a z%B4ufSO&03v$b^35k%Uy?Ky!mC$LZ1)dA)pYutQ(kfp@}TvJ8q-la6Tgs4Hy6vru3 zEto9$s6RKP5c=`kSmBIj+{c*50(ENrh?D?SswkL+&yr%-*Z%r;hyeUL{ZSJ=YPU)c z_6vI>GpMZn^?scST3YsQaj$+J0b+dp}I@GET=!dHgN#;5+Er;VJ0Z!Q<>(w5BpQx@7IQ$B16JW@7Gwh6qGde zW(1J+0@cOT+}=i;9Nrz#JTw`NiGpjty+vP=(W z``><6p#SpLvQi@(W{8_F~KNu@&D2n zJ97dCYCZek9<=e6f%ntq5c||TDPNEe)4MV#geOc4&$p)dB};C1WE@LTM|xh&DB88D zJSCdZ`~W-%r%`|2^9Vj+&e~!&dL>u4}K;7eHYLUNc(b!7uT~{-z);Wo{6dBX08wpVfYYo!Q5|u`{P( zS9IQF8tg|3pGk3o?=oMF@tT{=%UnJ`UCA503i=H{5%Hr6+9P-0mE(jQ;cP_%5EG=> zwB1B(!A10DpM}4p9f9Gyc9?~PKW#+-!X}ni>lM&*(>^?|p8EiFI+a3t6gMq1^TCH6 zCRSNON_0Q(-W$CusocSoS_S>t|BKfSBcv|pZkO^t`Y?OvuNgz5{q}|3!fvNOPezfqU zl;G`e7xhKw*-|U>ep{ye(fcBQl+GhqV|?|V6W{POn>9>2(IJ#jqyBY1*Zl9h!~T%A z@`JzBzMK}a2Xm*?08*dx)UUsNHC-`JvUWy{#YC~7=QFyV<@dZSgX={_%*6hWv*l&_ z^4&jJj^%E;sHu?ku|-Qdv8?<+jDZAzY&xyN@qMW*pTydf;kWb9sajq#i^Vi;kYNf5 z9-bIj5f{go;kL_*+V^hb)AX;#85P~3^P$pUM}=JvnkZD+T_AFgcF;~Q)(kEe=8w@* zHR4#O&i|ig^8+0lsy4oz{W=&jW<<0fwu?F=6H|krhUrE0_<(!|`)WMI;B@;8Q34`! zV=};A(kSYLM)AO;N&0~AO~Qo=@n}H%+JJTxQA|7qbfiBn`70Ld1q@~E@>o`^1YS#C z5K=uvByS+AK`?>k7b`DesEU5O-$mzn#v6saqf!NW1A-^f&b!Oo0YW;1AXyg!w|c-~ zf^%vfsnahn=w|+%EdS3A8BtG#a-I3Xk>H0I7=osue7!i@hBO%Y@-Cr^NLXuF;|?SM zmq*w}+%RNwr2}1yX*o}+?yF_AU2P|v7VQ8V=mf!=5%vp)HwYsPWW-Ad0-l=u7<2w? znF|r)R$1P2%v3>34MSa3qivS=uZDIHcU!1H@m1zmY?;HpK zABIOj@@&^hVuHxB0doL4AJyE!U$9k8Kye1&7{nMoUf65XDAM1!# z5_ED5J#fByX!gZ@1w_gSeuN(&M+j662!8h9%xX6*MfaQgHuN8{ke31C%}+{?OTmnK z7hyAl_8w0ht+t`3pybsOd_NTEACaMA#Ncrv4A6sB)LU-hnpWo+(R6agFs=#Qmp~ZE zO&N4u7KJ&is3z?ERQYUzZqH!6-&R3OzYzv|adce!KUcYApV}khTf*@QGzPMidI66>P|3fNV(sx4fKT?`t2F*Z`@!@3ATvoP28R^uhNsamP67^aoZkgcgO8d9VeG)fb^bcl zXY54YeIt-J^TW0OMsPpR0DwY^>6Z<$z|^`RZw7EEL##&L=nPar7{r`L~{xte?6GWuvl zqE^*fv07sDH&;jsw0y{**?jXteaNIM5g?T~c=|f`^NDE}cWy-X+qQU}?*1{rwLge& zmc+Y}5ZBXqI4hf%-9i~a_6>1i&+NE$)JBp{jr{^XB&b5jPXE^5dl}EX4ErWaz8_y) z5L_}iz|Gd6+;q1ZqQ7%1%F=&<(m8TI{kzw zOCo$3o{rvkmrZ>>+Gq0#tzx>(p0=n?+g87op`oExgp`AvaiA|B+;WRY_;ULtx{{y- zwogB03PWvyAowHe-cwoO?BNa=xWq+Pr2RL137fS%x@W+QvT8bh0`5rh0Co&NIJnwW zvgj3~5@mH?jLmrOE*wyVuItnDi-1@Ay%;TrOxn)CaDbLgt21KC40bqvuoaWQq!|NI zSR)Gw(3}DA!eaH>{eRafgQy&pkn^kQ^xriq&@r|=ig)z^->ZNW=jF?UZBABmpXs%sBh+2Q{{RuWl|#=Oh!o64VtDyN zZl!88>4OSIWAsEj=)!@K9cik;u7VQT3$Gt9!AcE&a?Cpq#wtAfKSw6Om4gHr9ZPm} z@JM06HhQw$#2$`oR*(dzdBAesO|>TU7sMcQqCi#_qPqtRRDQUdU>ptYD;#JDvv#Mt z`8@O`%kd3FEVlJ4~R z2=6pr_u}Eyv_~5ViGW~n!^Gn_7#a9?J%4ZynP>zdHy2Fh$D0?|`qKw(o!!rQgz4>B zRYjVX8#t_FIQ_i%dY=0K4iC-$4iAymR)_Y_a$7MBxI1vEZh-{a=Nc}5gsb477Y3;> z%{N<8K*O>ae|L?B03-J=yhBlZi{|s&@a_^}6>K2Cf(^TesB|mG_*n{gnS*H8Pvj#K zj$rC7IHSR$tOV9?F86xjGgNo{8_*jsSG<;6S&G*+20QSsvI`HFO)_H><#{)cF}JyZ z#2%U3gm&I|l2-#7TOTVb)Tj)Cd9`Y5aX zQ)}6F+)44cIIdDwH2u1ly6KAH*?dyatZ!oz!PG}gO1fx$v20xfLS1gSmTFGc%%!42 zO5FF&Zrr#bFa5htF#U99Wvjun(d7{C`!mAylb^_1(ajz;+a0TJ3eCt+KN|p=&oPK3 zMMwkuhkhqI3V5?KZ}EpSjodmbab03;Sl8ZM2){fEX9&V(ctn?4;Sb3Dz>Vom6ad3e zn-~9ffmp$I0sK+2A4!AvbY})&lFJW|r7!Mb`}yG}>y&+TMdHc?kzJSETii1>NocLZWdD&A5S;uTOk;AFdj?` zjfsNuG2j~&Fdw|D?{k>pYgjAzl@c!|Y3YBlB(W z95;bmdhx7_wA|a{e7ppz7D%c=LQH%GoZ)KEj?*uBrmLaY-1X3eetl|P|MKFds}Q?= z|B>FAiEa?qoySUFY`U|s(sFqgxZ8KN+E{5Ye?e#gG-`-CEY>lTR(Rab@i!d%t1PGA zawa2lMlv!o@+DmA4NgxH&?k&Z<&3P_065>~J5SyHlpGDAxD}pRt9Yh9*spKKi?#nw zRR$+p)kV?_xpDfyC@WQ(%Rl&T97%5jSbv7KO}*>p3LEy;U^eWMBl`^#H?)Wap;T1F zO05GiOY_=3Z|W=W%~QAwVZb3PD+{Wgn|=r~6S-FS!3_p;Sa14-5z2LHC1l`fOOrPUEh zUyy&0TK09B3}EzulK3oC+y@!Qk;mAdPZ4mb%6@A-T@^Z7po-!q)B6)`!~nGc_=JcQ z`jvn~=#U6k)svaL!kGQ{9Z1 z-Y@&dRrdRJ*JofbtaYl=95yAL>?5hDuf_xS;pMD+|1Xh|WZ(e^?B9l;J)Y4uus;ET zu)S@YtW+>gy}?}wWZ+y`1TpK-Iq6p{2FU10)xPEcSw&rTNTmWJfwO=L7a}ll93@32ba% z8JWeBHTumu7D7To`s~2W?i#)E|Hee#@ixFj3!M}}CVeJ77GgCqR-!AXs~ZhrgeM$R zqyI`2gABrW#7Ao1|62qrKRVghBg;QZr54yoPJ=553gt6I{IIO~m0EY%18(1V7$j01 zmIQYf1Z(A9<(xr5#1BoU%_gAa7D;e&dTLr-57B)~%N5u|q%H@4Ge*r2&`Y@_mP~ec zR!K?thsN*4+VyxMHj_s2+XvOnvj+`KLEcQvK!=Z9RmN6VNoY!)@O$>>`N_fb=3=zR zg$Y!Zq%XQIkGKbnqr=_)8W;-~Z8fyqZx+>)jCzapuJ(s?lrKirc>*})pbE9?R*MGYr^Lt#^df&M$SF)<0tUNy=nbG!Fo8>_#BIYIK>%xg=sB(ZlNpRB zGGYH?gVtA16p!1}v35+^xpC`O`wyLDyCEKkePa}>s;f}k{V6=rv-R&$bu6&$A%vPg zsJhdJocN*&-(YK4%e1iyJBzQy)tOuFsTADgO>HZ?_V~>1KU1S;E-dr`tX~v}uA4@I zb)0F^Z_Mykl4lBv5yl_91U=?N__^L(HYp&u3Ge5sdR<*~P>OeSjUN{P%o_JH1c_H6 za;S>8D;RCVM}u@)Tv{rZ@O6muWMoZjOzE6S=}e zO-&61;cMgDtBwS9vmvb5e;i+Hak1VE!ye4*ykvyP;6V$mRNJp#6!HgpDxKq02nFa3 zXx4IUY1BK1)Ld? zz|3N%CK5tM8&K^heR^@(4>Q>PNNo>FKmfNG9y=V_Jn9Qj+)M4Ch-@BNYLqe#sC$Vu}h$c!G&o$fekDGB^$)qo%edC-qZA+DH=;Son=^eC4s~IuA3W9XQu`~ z5X;%`ZJEd7iCG7}h-%;^H!mI@M zFT*(_q{WPwSBuJ(umGfJC@wC3G!$+{7vxIXMJ?-)Zaiq8AY@G`m9pJGGU#RZoQMB% zCYXPJQBK`?<|cQW!Ef*OTIUhtAgxGR9@8l5TVKR*t_ta2H2C%NS)k1yAG~L<6VrY6 zSzsu;J67Frl!>`stHC^85wETE)~QN?5&JtqXBMfE6qN$mC_|!JkzY|JQ7z$rMJx;r zu^Ai&mznw|8(p{<)KRxjn& zHL{vZI{wrC-4xpsO{wSkF^S4HMih+I(#pQBvYN&EL!;ai7l`tOK}qEEk@$P%XwJh81~DN7K&zX{tqas~ zU)`qNDmte>kuF#Tt=u50y}D#GyvDJ|fuZKnj!SRnN6lZC`i)lmU6knhlrTOzS<~iZ z3wuz7xC2~t`&?(9Ly@*+6%^QJ-B&xiXanmRUWV}}(R{B#9n{M!$}a}Na}>iFrjhtH zY}!RsONF)m(_A)&2;E&YYpx3}*&>xb?p{W|gUY;0tJ-YcCl-oU@bAJRoR~~ZGtklQ%7ICLao{;2stC+uW7%x%q}SrFSzrJb7~<2gIsUPmX6hB zj0v3u4lhWEJEg_iesHwPJvz7)i=ZKKE}ne3Eh^tVFreR#tk3tmyZyj6Vp zy%~iW&L0gc&+w9XK%8)V`(r&r<(Ka%lkI2UvzuQgEkdqoz#Z1t(n2A96NzVCb;DOx zee<4kCCg@h{cb(Q=hcf$$@J6Ab}1P0PvKBllR>|AH$n>KQpVXw30W z@NF;qm1%li!y@xAd;%j%K}q z#~-k9a(4RTJUHrAZR&@bKp#^7t+A!%nz6a&;pqeiKb|lToGcIm7I=yA;^_`gIbh zpH4eHxk!AQ$YUQkG^7A&+eH|Xlt4eIB>ntVXIEG4Z~q5QeVTJ^aJb91B<&&;8mN+o z8iZ@N!kIMyEy!Mo5e*CskWLE1x2mE7+jbz^5+`$6(}o$S6sqa5f72*O8d2#`2HU0c z*rDUZJV$^CP^yGMZ=}G1WQRy68*c8WY;0|is9Lf#Y{6LCU)GC-Cl}uJ|E7B3&aMGM z1bk+?S}>$a6!Ji$fEuP17cHi%);M(-A!%0@J-fg*5U)`G>Z=urqO|KBe>)2nsFo}v zR10X%W~vly!?xSbe&Tks%l2rZOHjN~#{{&JXY0)ujEA#_27tnbcGFX+$}z+37lpJh z_y-Zo46qUAgSB0# zsm;2b9+bMDe}Hj5hGBN2=_*SE9Adn`7M;LjpJR!GQP;L!H)TzMsWr8MK7WXqIYCtk z1x^ULDg~vmaf|EL9xrekZ%jpRokzf@vxD!pvo|6K3kXQmP^iU$iw3bx1||ep+<`a3 zzu*5ej)m(HS*d)Ef8QW&pvf}7j^Oam&COPogO9E^d|oiilk(ccfRLM(%;^`JH^r}3 z406NCG9$!SRLkRh-(tQgl^&p6Rnth-TfNfk4*AIs4765=un;E?(`p0WR2Eh3{mWAO zl~)&ga+fjC(tBMpUJEc2B3#(HOIK2S?7z!4o!4%SxjM#P&wp$f^6wv&r#KBq z)>T%??|rMma3PBsj9`d9@+^7J$Ac?$^jf7@E7Rm_dmkXQL@+{b&suq}*I{G#zd?24 zAomG;DyX;u^27i^13=@D3zoOBp`ov1{Qxot&oQpmK54SfX_$VtNHF0$#K z?D&h@#p?hE9fEFV%tJo+wxa5bKxnolwBrhSzi(XUf`xjwX;ba}tX)Pv<==7Jt|F+| zeqV&@2RWn!3tzJ06ic{{0BJSIH-F3myC_^SY1nzzVZ8Liz8SpL z9{B1-AU+tLpFF7DY$D%fY__|<>9%<~(QvF1$HWr^E#`9l4qNcAEw`9+WrP-q3uZfJ z_NSq@2<8L2UCo{x=C4{E9uBj^3GG;J)ThE}H6vQU(F%4u^E~H!&up~Ap}F@F=K-hP za-y{XgqjUU3s%4Yx(&QcI7KzjE;ctE z@LB7TJK=o;>Y?jimWNEEPM3o=g!a8iX%CLq4&YF{!M1MM)yiVHM=y1U|8ltX*-tY{ zq_ij93KvZ+>@^Z{YuhpP=FX@`5<~MsEmCx;{bNP%?UA<%UG9a4o7};`lx-dvAD1Jg zE7WixRrqOmdf>9btaSL_yvzM0%b+570aX_z?-n>GpF$ZFm;b9zCcRp@F^=A! zC>oNcl{LY}XrFZ0LKMU7xs%v|sGl>cad4t?!g}dW6F5aKQb@WM3(MU@hlZX)bz>S# zi^+#JI>8LK7I5D;CG#fg-;*ECl!xxGTZ>`_;JW@gMEr=l%TgdMgex5m-v>2t;!ZSs z2tg0`wr7T``A3;V-)(-cn5jLR)!`+mnGqt7A9PsWOMT&b+Y8h@tDmD26W*5FqG-WC z5(CoRWTBHRM(+Kel}>3db?2}2#gn%~{l2fxVI6tiPAh!#9ncqx`Jmkwc$bBrl;I8> z{J)`epg&ng9jO>Wn_E3yjXiY&^aSvetF+8kv-P8fA1Sn zNNDBix_QLP@So0i^KUmQ9we?+>BPkIcG}@*U3)cqkTZ!b_1dViK6|;Ay=jLZcie&Y zdK{0!;-pxmy$#`YMdvRmO*6f?^pQSw22! z)YM69XFvnqlJMA*aNQ2p1-tX?Z`(>QKC|x0OCRnt?PsK|sGsYs5|E;kLEdg$?iJIk znD=s){7q$&$e(%DfA#BFStAFD36iqbIz27VCF`~E&{@=TPJePH_zi}TVq5m?Viz}43Q*Ms4~gq#9_TwwFCnmaH2rHaCLC1 zxdKf>Lbr_KK=$mbqT5Y6xHt~;i3_-&3fhhMc-(xKCYbkomu8*}q)O*IH2#}L6^^ac^*aNS=Q;hH9-3I=LV zvfal@X`SnE;+j^nZ_;CFQNC*_B#pUZdQ}cX;0EcViR#>e0WQm>9o)uiuFr|0y;pn$ zaC>iB9kkz;-K|?|Y_gW?-=A-QRw&Pdo0zJJhOL8g{?2J*LwsQrQtxuFjQb4@-Gu4@ zyrslv$syr%q6%+ADn4+fDpuy54y# z^F+yC4@tD?4ZUJmgnuQ!k$auv5KN0Z?nkjD`Pww4@KN!4Vm@A{CnLwdA8511x48x6 zp3wKIuzQX^jk|SD(}VdUoqa%RBu#VA`h3hdPqyn&$xrjH-jNza?DHqV+p+S?zs zC4HC>AKb&++ZLW# zy1PZj+w)~zp%rYJyRQO_!J?*jhRDVG{?g5B4`P^uCIK0h6`OcILC4=O9=@_ zjVVazL`tNj1%A`#darkX@7g(M-}=;j?i<`&--Efk#p-npoY_fMZ?@MkG%v^;!e8DR z+x}KwW1X$`(5q#{GnbQ&dp!&5;_z}vIVHOrarKq=(mCBG+lyTS{!EY{$3h_MMEsOo z`&}hZlz%QRygegmIuKS{3P6+FasFii362#gRDsi(-)=lK)Mq%E+qR~-)$X85N2l<) zJy7JY?wcg{abpj6tNB|Mj~$8^DTc_v3$N^b*Yry5fPyVy%>0N zj5;o!^XFo9b?55koispPP>X7Flt7&4>9k^9T3Sf&SIBu7o47XOPqsq z>#zM!Xa)x1h$BeRxsf9ZzUT9&PYHkk)`@T_&pcmrpRE}S2$h;n{t~4`kL)(R5eZIs zZG~FHpPR6BS9Q&WQG}p$($_s*KlJR051(4;vb3|1KMIY5T3<&>L^;2wJh)NjV{ymH z>E*2%sQbg>2H#(VJqU^D6Z8v`eKS=dCsU+=jfn|=?{8$<=X7=GwDA3f5z{qc;ss$0 z4Z)SG3C+QSV9E~XdHZeylZtp93?kjfBANa*2WuH!v61C!dvi^xG%e)jjc;v==u9T% z4@j%`-G3#>kfx%)M(?!w*S3Lc-K)jzvkTd&={cs!G8YkEAixm6l@vr_(B?i@kcPD( zI~~dLw5A${f-Kb4-uT@9=oj|)WBz6uPl6A2YmAD7d&Q%pJbp)|O%m2gZYI`9>)8@1 zpVe$DLrNM%YsWh5Khd+5`17RAH;7!BAqk&*M}Ux)*bK_C6jvB#O>=3lw0#OAs7bb( zXuAeNN~VY4xY>;!FLiU<*`%7~CBBkg)0bKbGBGvt+!6#Ealn&OSWg@&wbNbTZ|Al* zd)kFy3LPb=^OGLw=Gn$9%c!+eVPbxWg)zEStUAJZ9S=I2cMtD;fp>oga$uZ#@m^I* zpXs&x^PAkU3i;tbKhG3dR(}nRzLlY0S&6n-Jw4>dwD2@RR8M>A0z5~X8gOM{y>7Xy z`8?~%d2Y5Ewe>~vIGjv>)NS(Fi`NcK4N~jKss{`qgm#V(r_DRtbB?1vfb(TFKXqDB zyaO7}VK1hio;w;)+vDCWeq7&r;f|JTM%@s&2NZuvp6tiCUSi$NW5P1Iyd013BXo?W z(jh=+rMY@OT8GA(pUn1yPLgw^u$1vkY*38&xCbe4@J%3teWlwE`r`YsTMN-d-`^L_G=)wV|5fE zxM~mXb--+fkg*_W9?a+(=ES zT!K;&9?l4bGE?%e!>BpsGqDz;Ld?t#%%H8lj?dmE1{VkR^J0x#M{wR8s_Rx=Tj5!ga@&Wwz2ZlIe=XJZLG9Z(soETYlw9}@C_@8~?;&X)<&0M82@eX9V z!%FBtBZsCFvxL$luCS)0%)^;|*SSJttD}RdmefP-$7tlcyo(|1 zUV>9RRQ|8bSeTY^33Y;^r2>e`5H}$CVTaN4ELy^Qw=1z+%<@}MW2V}TdTdw*hZrTv zu=!+=O1Er+wd|cy>d-uhr@Xz~lLM=5etWGq5V0YDZ)11qTKRyTyBC>UIe_@R$fsiWFZ*!P#borJbz{;J@pCK2|>Sw5|15q+rPFT>ymxN zuge*(MPDQH8JHXnOOq!E7NzC}Wn)Ez$Qy|;Sk|#-zY#NppES$kTve`SHVMTJ4{ng+ z4M{MGhwY~5)=06{;9Qnh&fXqC&UKc+9@1pd-H(HWK9%_aqqq|!T3JT_1>YQ|h3_|Q3fs)TPGU_~X^0R`5GG|bS?!=oZMt5h%wQ@acpkn*e zo4Yixg_@ueR zfeKfxGu!00LG)z5KZzIQTCSceSOMl8^zqmsT9aZ{uZX<{8EtF1+EBOD&8J^be|ec| zDK0eD0A}?Qg%4&^_maU(D(h3~MplK3zJKi)a1VOBlS6Z2reSkqz~h~S8V|m$0gL@$ zmh?f3e|@v5Z}7O;Gy~k>+YJ@Y-_WO zBQS2Z{9e2@bFchFS?-xj-oSCo`~ILHh3*nqj;(K>dml}QLv3{(?m<@;!&#Q|jCj!_ zQ;;^HFue}B>$M3x!88<|zP&WT@ zd3f_}%^T31YY4#|>Wc@NmZ@m{v*JSJ2CfYpr1TMHuyf)B&mvt!DFEVh(Rc~3A}8*P$w_lo3)tFd8?seQT=K6O*`Z(hklj z^8w*!-`X0?NN`vTGT<_(v%B3}g^d!+sWE!HURGp*T*0Hglq*~fFeqdGZ<}*uwAc-6 zD9UY=4Agz84QHXw^s@btyK?8rd%x|J&$!H+zh5T5H3>1ojOf;ane^V&9R>G z*HI&4_;$n2JIT(%^YVN758d|7Hwtt-CUWse49&DPBRM1RXGBPHylkdgEwkM3?!Fl{ zxrvC=I=8F1@xxYhJ)C#noL7Lz%nGNiq4{H!r(X7ayCeCcYdmYwxZC*b97qX+mJ>ks~B>W#;U5o)AQtq zqY?!buaG3e_7HA!QF&@R$b&KY z_<=yJA1kMlscGz@Y0G>v$R*LegRy82iw78`-*rXufh3hby)vAK>z5Fs(7LX=RZy;K z&X|Ffl9fN7AzWh%#G?-((Y&Ik>Zx9X-N-pk1fQ#Tpt5_*RpW9}1cv=T7!q-YL_Gep z@Q(Qh>Kjr?2AppTR59jWV61(@^HWVPuOJ`x(f(_yQ1__=iv6x;TFj18cQ5@>)9PjC zK+%Zs;5Um;pFD*%7iWCmRmF5^MyTOpEN23Up7U zFjfXE-rS3%@Q>`X-S7geUeK8kx}uIBc2|bghjowqwhj_rR1vw-%M&NTpzSg*^8)_- zsO)*BTnGKN7u`qm6GZm2yR*wm0Gru+72p$~q#nC|0dV>Cbrq8Xz1xTRiMAV>s5&k- z$*7B(7H|?vFp(VaNM%3!G?~qIHeB|5zV2TPZHD-94rFs=dYxXw|(8(}@v} zB=E6=Ze8zq?-;s->?@EuZ=HRV##bI6|C6ZUTqtT_eN*PwMaJ^hBB|q6Yjl+C)my0k zVYD|+3v@>4|5L0u_aE(T*0TszuSiAS7Gp73rL=gH%FTtAuIEG@jGuR_65_)HpJLESbKJ;yND}yUF)`7p-WM3el)Q@OEuUSn@t$mvW(IQ5%(^)hRLaY%L9gYW4H==m z*E4U@KJfTBmQaf5CIFIgqW$`g!%4{( zygCQ-5&dm!&4I7*1Q=OSX7KqJ&R&Ww3noLp=|+#MIS>M5-lmW_4c_;Q8hjA!%pLRq zA#*t_7e26PHs&*YC`GY0p?Xrj{$0&`U;FT5uNkp6D8yGEC7POC2g1j&SU08o-~U;v zN-xF*+pXcwCtk?6=2ShUc%Dor;aznVjG(UPHZU=}uVA1kC*FbXu6Hp0bUA0zI)%Kf zP#nD+=(vfimZe$Zi?{xYHR*7}LsnOEw92N9#vqoHTmf46NBD+H;64`_3nIAWqpaMV z_~qf14UnPjwtDblV`o&Em(_93-@C{~RfrJ=byVvIAX%pHNNsW2rK-<+n#pbYTdn;X zciU?t1a&Wwqk8aoNMO=EIT9{*RcNpPS1ko0?DgkX=~H--DLri|b{%yO10;~&#n$mQ zaTSUY1<&EH8{vSZW(I0gVS%wZt&GPtg)v5XCXdO0lgA!WM-_g;A}9cPCK$`5d+Lr0 z-(qCIlBAc^(#&M2sCFgqotIhCQI&>@XcuGJrxWh}aU5L!BLapN+lCt2KPvM3ve{$$ z_tcnSjGxZ9GNiGo3uOuE(H#A4A;t+`708s`u1@N7YpHFmcR4qZ#!Dsh8!{(8+>BIh zV1pyIBF(A%=Y{Vbs+teI!7%c1b%WSSpVl!^GCwkuSyKOmdpmz=W>+N5VuuN9*T^88 zbNVwJn1Xp9Nab|IC0`Cb{Rjm4B?9dEPMUn8Y)tnl?Q*<4IYxB2;jFaPvcd1UJtjsr zqRgJAKWZNFf$&s@EQlk8#TOm_sj_#?v(|Ko9Nlf>^y3ln^n2?9#!+(YLWegUi3f;> z8TEyL)CrnjLnT8UIlyI_R2Dwf+g_iPzZ2U0rV8=J0{GO7uNDsYzz^JAHFV_2OTX9@ zDV&C)zJ>N{y;iAbr(ye8HnSA7CGSTD(tpICa5F`av`BFDnt7S5s5FgB#$GY<*^Iw9 z;`W7DfZ9>Glh>an$QbMBC6*8}bGiHzip)!m=FNVyW+KsfL;`m{Z;D~>i$(-~)*!hc zRVKFluFSYX$&SNz*EVTyx}~fYj*j~(Er~oH6CU22Y(r5%{{V#;24=9~;X~UJ+8|^L z^OM8i5bI~!V9Gb18y5e@j8&U^!$U9C+Mx)ZwcZ@vRW{s&AeSP zO%Z#LOX_`=6{{~%)b*svG~hUFU`O(l5)HR**ws^Xlk0?MHrImm+_X|?S`o{M<4Ch` zeeS^5waa4-|I?lDNY!}P0`#&e{D&uUTA6G$1K!bLty6`)%(N#=E>hw8Qnt7$y^a z#2QVb{4jkr-@iFf##yy;%Akq5r-}i=kh)ijb)BzI57_(%sN(UQR2Ik^Xe)NrjCIRF z;&mvi*dG!TZ3=oYAl&ddbIs+GN6VlmOK%Yw#!4^W8cnhhmfolrF%jV(xN2UyT9w$&|d;**d6_dzcZYi zaj_r(BqAd2PIQldxTP?q{tyzc$!yGhFi$qC@x@R4UJlP-6_)0Akn9rdFu}?8{>O zroK@uqK(f9g|zVQ6haDGPF}jNvb@fDDlJC&!E>TCQ++~Ak3rkYgzMO1=#ZZlf1m^K zr|@lGB@R|UA|tE^oyuqZn>N@G==UT!g@co_7HjT(!XjDq6OGZr%o=Lrmwh-aL zP*t;;*N3ESj8^VlyJndrptRVp8%*m+?^!rn6UbN)hlMf$ROgn*aP zkiY%l0C|&4fTII~RVf+2PF7AzT^mFY`IP%pzOVK-jraQ#-(47~9>|~e_3|eB1`-C# zYJLMPNwLT;9F!A&F{I&FfvwceO@yxL3Mf%}zY?cYjEu1gE@z_VhtJBYMR1WtT{!$j z6(-K`!oM6;(1f9!SyzilSkN?t^126gEfnGa-2IbYZJ6?U?Lo{dPdgu{1Iy_bk>qfD zC3rqNlhiDYO65Zb3IEN=7(;1ZQeI*+S4y;Wj%qQGrOqyu{W|I?aN}}pr5$nvfscIvCbDFv zY+bv*tfK+7o-wLpI9e1~7=_&RU4Gx5;`I?1POwk-%%#YgJifVNg@gp5pc{0HkW zf1${uk?Bl5%~ucxMA2pZ?`BlH{Ujxoekye)&48-kgwA=YHN<>jqeVeZBZ1YK3`BsC z#H+c9;A}B_dain}2x&pXNo9k*`8ixXPkq(`6!}JF6gcInt=Na zk@%s*n$~)sv65QwP0>kKZGQa>g1OPwqnqZb)^HyUo=9H`TVEmSZ;F=BI&qHk6D+*W z5?W?hJ#GH&^dk#YT6Btd#VhYi{J^0qCH`(%2VvhCZ)Ic>5;73w3KV#m0WdWeNuaUE z5vm6LbL|e{|Dd!-JRxu}0;EHh$p2O@x$P;+^(sd#)pY1J8AXYXss5Ky-_C(8&9vw( zQtk9onI0#_$1E9N8i~?U1!bH;MAQqFLwR#vy{P{5O!Z`s#nt&Q=DzA~Ke&FgVJP7B z1HWTrj~^gh_JCOeG!!_w*)KN-4}Vi+{%#3{W;2t;BW;!hg%N2S71}AQUF+ZAe6jT+ zQj(1(Ajiif>_fIo3I~n`9*<~PBP6|)My zvOl%Ebx%!#0lSU_z{Rd6u`Hh4tw{U5WQQJRUk@99{Qh81c&K{GxP0s|;&a429^UfS z@oSLt=t| diff --git a/python/swe/agent/benchmark.py b/python/swe/agent/benchmark.py index aa051da66f0..afb807dbe6b 100644 --- a/python/swe/agent/benchmark.py +++ b/python/swe/agent/benchmark.py @@ -3,12 +3,13 @@ import random import re import time +import traceback from concurrent.futures import ThreadPoolExecutor, as_completed from typing import List -from botocore.exceptions import ClientError from langchain_aws import BedrockChat from langchain_core.messages import HumanMessage +from langchain_openai import ChatOpenAI from langgraph.errors import GraphRecursionError from composio_langgraph import Action @@ -20,50 +21,116 @@ max_retries = 5 -base_delay = 1 # in seconds +base_delay = 1 -bedrock_client = BedrockChat( - credentials_profile_name="default", - model_id="anthropic.claude-3-5-sonnet-20240620-v1:0", - region_name="us-west-2", - model_kwargs={"temperature": 0}, -) +MODEL = "openai" -def build_comparison_prompt( - repo_name: str, issue_desc: str, patches: List[str], tests_passed: List[str] -) -> str: - patch_str = "\n".join( - [ - "=" * 50 - + f"\nPatch {i+1}:\nTESTS STATUS: {str(tests_passed[i])}\n\n{patch}" - for i, patch in enumerate(patches) - ] - + ["=" * 50] - ) +def retry_with_exponential_backoff(func, *args, **kwargs): + for attempt in range(max_retries): + try: + return func(*args, **kwargs) + except Exception as e: + if attempt == max_retries - 1: + raise e + delay = (2**attempt) * base_delay + time.sleep(delay) + + +def get_llm_response(system_prompt: str, human_prompt: str) -> str: + try: + if MODEL == "claude": + client = BedrockChat( + credentials_profile_name="default", + model_id="anthropic.claude-3-5-sonnet-20240620-v1:0", + region_name="us-west-2", + model_kwargs={"temperature": 0}, + ) + response = retry_with_exponential_backoff( + client.invoke, [("system", system_prompt), ("human", human_prompt)] + ) + else: + client = ChatOpenAI( + model="o1-mini", + temperature=1, + max_completion_tokens=4096, + api_key="", + ) + response = retry_with_exponential_backoff( + client.invoke, [("human", human_prompt)] + ) + return response.content + except Exception: + return f"Error while calling llm {MODEL}: \n{traceback.format_exc()}\n" + + +def build_comparison_prompt(repo_name: str, issue_desc: str, patch_str: str) -> str: return """ -I facing the following issue in the repo {repo_name}. You have an older version of the codebase, so you your belief about the -codebase might be outdated. +I am facing the following issue in the repo {repo_name}. You have an older version of the codebase, so your belief about the +codebase might be outdated. Some agents tried to solve the issue and generated patches. Your task is to choose the best patch that fixes the issue, +or the ask the agents to run again, if you are not confident that the patches fix the issue. To help you, I have also provided a summary of the +run of the agent. Issue Description: {issue_desc} -You are given multiple patches and you need to check which one fixes the issue. +You are given multiple patches and details of the agent's run, and you need to check which one fixes the issue. Only one of the patch will fix the issue. {patch_str} First analyse all the patches thoroughly and then choose the best patch that fixes the issue. You need to consider all the edge cases very carefully. The chosen patch might be more verbose, but it should pass all the -possible test cases regarding the issue. +possible test cases regarding the issue. Choose a patch if you are ABSOLUTELY SURE THAT THE PATCH SOLVES THE ISSUE. + +If you feel none of the patches fixes the issue, respond with "RUN AGAIN". Also give a detailed one paragraph of reasoning why you feel +none the patches is a correct solution to the problem. Analyse the runs of the agents as well and provide what the agents did wrong. The resoning must focus +on the aspects that the agent should take care of in future runs, as well as a summary of the patches generated by the agents. + NOTE: ONLY JUDGE THE PATCHES BASED ON THE CHANGES IN THE SOURCE CODE. IGNORE THE CHANGES IN THE TESTS, DOCS OR OTHER FILES. -GIVE PREFERENCE TO THE PATCHES THAT PASS THE TESTS. +RESPONE WITH THE PATCH NUMBER AND REASONING ONLY IF YOU ARE ABSOLUTELY CONFIDENT THAT THE PATCH FIXED THE ISSUE. RESPOND WITH "RUN AGAIN" OTHERWISE WITH PROPER REASONING. +YOU DON'T NEED TO WORRY ABOUT THE TESTS. ONLY JUDGE THE PATCHES BASED ON THE CHANGES IN SOURCE CODE. -I am reiterating the issue again: +If you decide to submit the patch from Provide your response in the following format: +{{ + "patch": "The number of the patch that best fixes the issue (1, 2, 3, ...)", + "reasoning": "Your explanation for why the chosen patch fixes the issue", +}} + +If you decide to reject the patches and run again, provide your response in the format: +{{ + "patch": "RUN AGAIN", + "reasoning": "The detailed reason why none of the patch can fix the issue. +Summarise the patches as well, so that next software engineer has the whole context about the patches and reason of their failures." +}} +""".format( + repo_name=repo_name, issue_desc=issue_desc, patch_str=patch_str + ) + + +def build_comparison_prompt_hard( + repo_name: str, issue_desc: str, patch_str: str +) -> str: + return """ +I am facing the following issue in the repo {repo_name}. You have an older version of the codebase, so your belief about the +codebase might be outdated. Some agents tried to solve the issue and generated patches. Your task is to choose the best patch that fixes the issue. +Issue Description: {issue_desc} +You are given multiple patches and details of the agent's run, and you need to check which one fixes the issue. +Only one of the patch will fix the issue. + +{patch_str} + +First analyse all the patches thoroughly and then choose the best patch that fixes the issue. You need to +consider all the edge cases very carefully. The chosen patch might be more verbose, but it should pass all the +possible test cases regarding the issue. + +NOTE: ONLY JUDGE THE PATCHES BASED ON THE CHANGES IN THE SOURCE CODE. +IGNORE THE CHANGES IN THE TESTS, DOCS OR OTHER FILES. + Provide your response in the following format: {{ "patch": "The number of the patch that best fixes the issue (1, 2, 3, ...)", @@ -74,294 +141,194 @@ def build_comparison_prompt( ) -def bench(workspace_ids: str, issue_config: IssueConfig) -> str: - with ThreadPoolExecutor(max_workers=3) as executor: - futures = [ - executor.submit(run_agent_function, workspace_id, issue_config) - for workspace_id in workspace_ids - ] - results = [] - for future in as_completed(futures): - try: - result = future.result() - if result: - results.append(result) - except Exception as e: - print(f"Error in future: {e}") - - patches, tests_passed = zip(*results) - valid_results = [ - (patch, test_passed) - for patch, test_passed in results - if patch and patch.strip() - ] - if not valid_results: - return "" +def choose_patch( + patches, issue_config: IssueConfig, run_contents: List[str], hard=False +): + if not patches: + return "", False - sorted_results = sorted(valid_results, key=lambda x: "PASS" in x[1]) - patches, tests_passed = zip(*sorted_results) + run_summaries = [] + for run_content in run_contents: + summary_response = get_llm_response( + system_prompt="You are an expert summarizer of agent's output.", + human_prompt=f"The following is the run of the agent after it tried to fix the issue. Analyse the contents and messages of the run and give a short summary of what the agent did. \n{run_content}. Provide the output in the form of 5-7 chronological points.", # noqa: E501 + ) + run_summaries.append(summary_response) + + patch_str = "" + for i, patch in enumerate(patches): + run_summary = run_summaries[i] + patch_str += "=" * 50 + patch_str += f"\nPatch {i+1}:\n{patch}" + if not hard: + patch_str += f"\nSummary of the agent:\n{run_summary}\n" + patch_str += "=" * 50 + + if not hard: + response = get_llm_response( + system_prompt="You are a software engineer expert at solving bugs.", + human_prompt=build_comparison_prompt( + repo_name=issue_config.repo_name.split("/")[-1], + issue_desc=issue_config.issue_desc, + patch_str=patch_str, + ), + ) + else: + response = get_llm_response( + system_prompt="You are a software engineer expert at solving bugs.", + human_prompt=build_comparison_prompt_hard( + repo_name=issue_config.repo_name.split("/")[-1], + issue_desc=issue_config.issue_desc, + patch_str=patch_str, + ), + ) + if "RUN AGAIN" in response: + return response, False - for attempt in range(max_retries): - try: - response = bedrock_client.invoke( - [ - ("system", "You are a software engineer expert at solving bugs."), - ( - "human", - build_comparison_prompt( - repo_name=issue_config.repo_name.split("/")[-1], - issue_desc=issue_config.issue_desc, - patches=patches, - tests_passed=tests_passed, - ), - ), - ] - ) - break # If successful, break out of the retry loop - except ClientError: - if attempt == max_retries - 1: # If this was the last attempt - raise # Re-raise the last exception - delay = (2**attempt) * base_delay # Exponential backoff - time.sleep(delay) print("Response", response) - if response.content: + if response: try: - match = re.search(r"patch.*?(\d+)", response.content, re.IGNORECASE) + match = re.search(r"patch.*?(\d+)", response, re.IGNORECASE) if match: patch_number = int(match.group(1)) if 1 <= patch_number <= len(patches): - return patches[patch_number - 1] + return patches[patch_number - 1], True print("\n" * 10) - return random.choice(patches) + return random.choice(patches), True except Exception as e: print("\n" * 10) print(f"Error in response: {e}") - return random.choice(patches) + return random.choice(patches), True else: print("\n" * 10) print("No response content found") - return random.choice(patches) - - -def run_agent_function(workspace_id: str, issue_config: IssueConfig) -> str: - """Run benchmark on the agent.""" - - # Set the workspace for the tools to run. - # import pdb; pdb.set_trace() - print("Running agent function") - test_command = issue_config.test_command - if "sphinx" in issue_config.repo_name: - eval_script = issue_config.eval_script - new_files = [] - for i in range(len(eval_script.split("\n"))): - lines = eval_script.split("\n")[i] - if lines.startswith("new"): - new_files.append(eval_script.split("\n")[i - 1].split(" ")[-1][2:]) - test_command = eval_script.splitlines()[-2] - test_command = test_command.replace("--current-env -e", "-e ") - for test in new_files: - test_command = test_command.replace(test, "") - test_command = " ".join(test_command.split()) - if test_command.endswith("--"): - test_command = "echo PASSED" - - graph, composio_toolset, test_response_file = get_agent_graph( - repo_name=issue_config.repo_name.split("/")[-1], - workspace_id=workspace_id, - test_command=test_command, - ) + return random.choice(patches), True - # get the git tree - git_tree_response = composio_toolset.execute_action( - action=Action.FILETOOL_GIT_REPO_TREE, - params={}, - ) - print("Result of git tree", git_tree_response) - cd_response = composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": f"cd ~/{issue_config.repo_name.split('/')[-1]}"}, +def bench(workspace_ids: str, issue_config: IssueConfig) -> str: + patch = "" + patch_list = [] + for _ in range(3): + with ThreadPoolExecutor(max_workers=3) as executor: + futures = [ + executor.submit(run_agent_function, workspace_id, issue_config, patch) + for workspace_id in workspace_ids + ] + patches = [] + run_contents = [] + for future in as_completed(futures): + try: + patch, run_content = future.result() + if patch: + patches.append(patch) + run_contents.append(run_content) + except Exception as e: + print(f"Error in future: {e}") + patch_list.extend(patches) + + patch, success = choose_patch(patches, issue_config, run_contents) + + if success: + return patch + + patch, success = choose_patch(patches, issue_config, run_contents, hard=True) + return patch + + +def get_patch_from_response(composio_toolset, repo_name): + composio_toolset.execute_action( + action=Action.FILETOOL_CHANGE_WORKING_DIRECTORY, + params={"path": f"/home/user/{repo_name}"}, ) - print("Result of cd", cd_response) - - # kick off the crew on the issue. - - if "matplotlib" in issue_config.repo_name or "seaborn" in issue_config.repo_name: - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install pytest"}, - ) - - if "scikit-learn" in issue_config.repo_name: + if "astropy" in repo_name: composio_toolset.execute_action( action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install cython==0.29.33 pytest"}, + params={"cmd": 'git config --global user.email "you@example.com"'}, ) composio_toolset.execute_action( action=Action.SHELLTOOL_EXEC_COMMAND, - params={ - "cmd": "python -m pip install -v --no-use-pep517 --no-build-isolation -e ." - }, + params={"cmd": 'git config --global user.name "Your Name"'}, ) - - if "matplotlib" in issue_config.repo_name: composio_toolset.execute_action( action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install -e ."}, + params={"cmd": 'git add astropy_helpers && git commit -m "add submodule"'}, ) - if "astropy" in issue_config.repo_name: - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={ - "cmd": 'sed -i \'s/requires = ["setuptools",/requires = ["setuptools==68.0.0",/\' pyproject.toml' - }, - ) - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install -e .[test] --verbose"}, - ) - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "sleep 120"}, - ) - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install -e .[test] --verbose"}, - ) - - if "pytest" in issue_config.repo_name: - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install -e ."}, - ) - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={ - "cmd": "sed -i \"s/__version__ = version = '.*'/__version__ = version = '8'/\" src/_pytest/_version.py" - }, - ) - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={ - "cmd": 'sed -i "s/__version_tuple__ = version_tuple = .*/__version_tuple__ = version_tuple = (8)/" src/_pytest/_version.py' - }, - ) - - if "sympy" in issue_config.repo_name: - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": "python -m pip install regex"}, - ) - - if "sphinx" in issue_config.repo_name: - for line in issue_config.install_repo_script.split("\n"): - if "sed" in line: - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={"cmd": line}, - ) - composio_toolset.execute_action( - action=Action.SHELLTOOL_EXEC_COMMAND, - params={ - "cmd": 'git add -u && git config --global user.email "example@example.com" && git config --global user.name "composio" && git commit -m "fix: setup complete"' - }, - ) - - test_response = composio_toolset.execute_action( - action=Action.SHELLTOOL_TEST_COMMAND, - params={}, - ) - print("Result of test command", test_response) - - try: - graph.invoke( - { - "messages": [ - HumanMessage( - content=f"{issue_config.issue_desc} in the repo: {issue_config.repo_name}. Output to git tree command {git_tree_response}" - ) - ] - }, - { - "recursion_limit": 70, - "timeout": 1200, - }, # Added timeout of 20 minutes (1200 seconds) - ) - except GraphRecursionError as e: - print(f"GraphRecursionError: {e}") - except Exception as e: - print(f"Error in graph.invoke: {e}") - - if not os.path.exists(test_response_file): - print(f"Test response file {test_response_file} does not exist") - test_response = "NOT RUN" - else: - with open(test_response_file, "r") as f: - test_response = f.read() - os.remove(test_response_file) - - cwd_response = composio_toolset.execute_action( - action=Action.FILETOOL_CHANGE_WORKING_DIRECTORY, - params={"path": f"/home/user/{issue_config.repo_name.split('/')[-1]}"}, - ) - print("Result of pwd", cwd_response) get_patch_resp = composio_toolset.execute_action( action=Action.FILETOOL_GIT_PATCH, params={}, ) - print(f"Get patch response: {get_patch_resp}") if not get_patch_resp.get("successful", False): error_message = get_patch_resp.get("error") if error_message: print(f"Error in get_patch: {error_message}") - return "", "FAIL" + return "" else: print("Unknown error occurred in get_patch") - return "", "FAIL" + return "" patch_data = get_patch_resp.get("data", {}) if not patch_data: print("No data found in the patch response") - return "", "FAIL" + return "" patch = patch_data.get("patch") if not patch: error = patch_data.get("error") if error: print(f"Error in patch data: {error}") - return "", "FAIL" + return "" else: print("No patch found in the response data") - return "", "FAIL" + return "" - response_prompt = f""" - You are given the following test response: - {test_response} + print(f"Final Patch: {patch}") + return patch - You need to check if the test response is successful. - If the test response is successful, respond with "PASS". - If the test response is not successful, respond with "FAIL". - """ - for attempt in range(max_retries): - try: - response = bedrock_client.invoke( - [ - ("system", "You are expert at reading test responses."), - ("human", response_prompt), - ] - ) - break # If successful, break out of the retry loop - except ClientError: - if attempt == max_retries - 1: # If this was the last attempt - break # Break out of the retry loop on the last attempt - delay = (2**attempt) * base_delay # Exponential backoff - time.sleep(delay) +def run_agent_function( + workspace_id: str, issue_config: IssueConfig, previous_patch_str: str = "" +): + """Run benchmark on the agent.""" - print(f"Final Patch: {patch}") - return patch, "PASS" if "PASS" in response.content else "FAIL" + graph, composio_toolset, run_file = get_agent_graph( + repo_name=issue_config.repo_name.split("/")[-1], workspace_id=workspace_id + ) + + # get the git tree + git_tree_response = composio_toolset.execute_action( + action=Action.FILETOOL_GIT_REPO_TREE, + params={}, + ) + + composio_toolset.execute_action( + action=Action.SHELLTOOL_EXEC_COMMAND, + params={"cmd": f"cd ~/{issue_config.repo_name.split('/')[-1]}"}, + ) + + if previous_patch_str != "": + issue_desc = f"{issue_config.issue_desc}\n. I have already tried to solve this problem before, but failed for the following reason: \n {previous_patch_str}.\n The previous patches did not fix the issue. Now try again to fix the issue. {issue_config.issue_desc}. \n Output to git tree command {git_tree_response}. Pay attention to the reason why patch failed to solve the issue and try something different to fix the issue." # noqa: E501 + else: + issue_desc = f"{issue_config.issue_desc}.\n Output to git tree command {git_tree_response}" + + try: + graph.invoke( + {"messages": [HumanMessage(content=issue_desc)]}, + {"recursion_limit": 50}, + ) + except GraphRecursionError as e: + print(f"GraphRecursionError: {e}") + except Exception as e: + print(f"Error in graph.invoke: {e}") + + patch = get_patch_from_response( + composio_toolset, issue_config.repo_name.split("/")[-1] + ) + run_content = open(run_file, "r").read() + os.remove(run_file) + + return patch, run_content if __name__ == "__main__": @@ -387,6 +354,18 @@ def run_agent_function(workspace_id: str, issue_config: IssueConfig) -> str: default="temp", help="Run id", ) + parser.add_argument( + "--dataset", + type=str, + default="princeton-nlp/SWE-bench_Verified", + help="Dataset", + ) + parser.add_argument( + "--num-instances", + type=int, + default=1, + help="Number of instances", + ) args = parser.parse_args() if args.test_instance_ids: @@ -400,11 +379,11 @@ def run_agent_function(workspace_id: str, issue_config: IssueConfig) -> str: evaluate( bench, + dataset_name=args.dataset, dry_run=False, test_range=test_range, include_hints=False, test_instance_ids=test_instance_ids_list, run_id=args.run_id, - num_instances=3, - # image_name="composio/composio:dev", # if you are doing local dev + num_instances=args.num_instances, ) diff --git a/python/swe/agent/prompts.py b/python/swe/agent/prompts.py index d7897f99e7d..a1d3e750145 100644 --- a/python/swe/agent/prompts.py +++ b/python/swe/agent/prompts.py @@ -96,9 +96,6 @@ - FIND_FILE: Use this to search for specific files. - WRITE: Use this to write content to files. - You have access to the following SHELLTOOL actions: - - SHELLTOOL_TEST_COMMAND: Use this to execute the test command for testing the patch. - 2. Precise Editing: - Open the file at the edit location using FILETOOL_OPEN_FILE action to read the code you are going to edit. - Make changes according to the instructions provided by the Software Engineer. @@ -111,33 +108,15 @@ - Review and resolve linting errors while maintaining functionality. - Try alternative commands if one fails. -4. Testcases: - - Create appropriate testcases to test whether the changes you made are correct. - - Use the SHELLTOOL_TEST_COMMAND tool to test the changes you made. - - If the testcases fail, you might want to open the testcase file and check why the testcase failed. - -5. Completion: - - After implementing the requested changes, end your response with "EDITING AND TESTING COMPLETED". - - END ONLY WHEN ALL THE EDITS HAVE BEEN MADE SUCCESSFULLY AND ALL THE TESTCASES HAVE BEEN PASSED. - - MAKE SURE THAT ALL THE TESTCASES HAVE BEEN PASSED BEFORE RESPONDING WITH "EDITING AND TESTING COMPLETED". +4. Completion: + - After implementing the requested changes, end your response with "EDITING COMPLETED". Provide a short and concise thought regarding the next steps whenever you call a tool, based on the output of the tool. -You have to use the "SHELLTOOL_TEST_COMMAND" tool to test the changes you made after you have edited the source code. -DON'T SUBMIT BEFORE TESTING THE CHANGES. DON'T PROVIDE ANY INPUT TO THE "SHELLTOOL_TEST_COMMAND" TOOL. + EDIT PRECISELY, MAKING CHANGES ONLY TO THE PROBLEMATIC REGION. FOR THIS, YOU NEED TO OPEN THE FILE AT THE EDIT LOCATION BEFORE EDITING. Your role is crucial in implementing the solutions devised by the Software Engineer. Be precise and careful. Use your file navigation and editing tools effectively to make the necessary changes. -Once you have completed the editing and MADE SURE THAT ALL THE TESTCASES HAVE BEEN PASSED, you have to respond with "EDITING AND TESTING COMPLETED". -""" - -TESTER_AGENT_PROMPT = """ -You are an autonomous code tester with access to a code testing tools. Your role is to provide insights about the codebase to assist the Software Engineer. Follow these guidelines: - -1. Tool Usage: - You have access to the following tools: - - SHELLTOOL_TEST_COMMAND: Use this to execute the test command for testing the patch. - -Respond with the status of the testing command, whether the tests passed or failed. -When you are done testing, respond with "TESTING COMPLETED". +Once you have completed the editing, you have to respond with "EDITING COMPLETED". +NOTE: YOU DON'T NEED TO CREATE TESTCASES FOR THE EDITS YOU MAKE. YOU JUST NEED TO MODIFY THE SOURCE CODE. """ diff --git a/python/swe/agent/run.sh b/python/swe/agent/run.sh deleted file mode 100644 index 03fd84e5c17..00000000000 --- a/python/swe/agent/run.sh +++ /dev/null @@ -1,90 +0,0 @@ -#! /bin/bash - -instances_unresolved=( -"django__django-13933" -"django__django-14053" -"django__django-14089" -"django__django-14238" -"django__django-14349" -"django__django-14373" -"django__django-14434" -"django__django-14493" -"django__django-14500" -"django__django-14539" -"django__django-14559" -"django__django-14752" -"django__django-14765" -"django__django-14787" -"django__django-14855" -"django__django-14915" -"django__django-14999" -"django__django-15103" -"django__django-15104" -"django__django-15128" -"django__django-15277" -"django__django-15278" -"django__django-15315" -"django__django-15368" -"django__django-15380" -"django__django-15467" -"django__django-15499" -"django__django-15561" -"django__django-15572" -"django__django-15731" -"django__django-15732" -"django__django-15741" -"django__django-15863" -"django__django-15930" -"django__django-16116" -"django__django-16139" -"django__django-16145" -"django__django-16255" -"django__django-16333" -"django__django-16429" -"django__django-16485" -"django__django-16493" -"django__django-16527" -"django__django-16569" -"django__django-16595" -"django__django-16642" -"django__django-16661" -"django__django-16662" -"django__django-16801" -"django__django-16819" -"django__django-16899" -"django__django-16901" -"django__django-17029" -) - -instances=("${instances_unresolved[@]}") - -echo "Instances: ${instances[*]}" -echo "Number of instances: ${#instances[@]}" -# exit -instances_string=$(IFS=,; echo "${instances[*]}") - -run_instance() { - local instance=$1 - local run_id=$2 - LANGCHAIN_PROJECT=$instance python benchmark.py --test-instance-ids $instance --run-id $run_id -} - -# Set the number of instances to run in parallel -k=1 -run_id="langgraph_agent_$(date +%s%N)" -echo "Run ID: $run_id" -# Run instances in parallel, k at a time -for ((i=0; i<${#instances[@]}; i+=k)); do - # Get up to k instances - docker rmi $(docker images -f "dangling=true" -q) - batch=("${instances[@]:i:k}") - - # Run the batch in parallel - for instance in "${batch[@]}"; do - run_instance "$instance" "$run_id" & - done - - # Wait for all background processes to finish before starting the next batch - wait - docker rmi $(docker images -f "dangling=true" -q) -done \ No newline at end of file diff --git a/python/swe/dockerfiles/generate.py b/python/swe/dockerfiles/generate.py index 8edb31b4724..cbcb2f086eb 100644 --- a/python/swe/dockerfiles/generate.py +++ b/python/swe/dockerfiles/generate.py @@ -3,7 +3,8 @@ import click from jinja2 import Environment, FileSystemLoader -from swebench import MAP_VERSION_TO_INSTALL, get_eval_refs, get_instances +from swebench import MAP_REPO_VERSION_TO_SPECS +from swebench.harness.utils import load_swebench_dataset from composio.utils.logging import WithLogger @@ -51,17 +52,11 @@ def __init__( self.install_composio_path = TEMPLATES_DIR / "install_composio.sh" self.instance_ids = None - if predictions_path: - self.instance_ids = { - p["instance_id"] for p in get_instances(instance_path=predictions_path) - } - self.logger.info(f"Found {len(self.instance_ids)} in predictions file") - self.image_prefix = "swe-bench" def generate(self): - task_instances = get_eval_refs(data_path_or_name=self.dataset) - task_instance_groups = group_task_instances(task_instances.values()) + task_instances = load_swebench_dataset(name=self.dataset) + task_instance_groups = group_task_instances(task_instances) for repo, versions in task_instance_groups.items(): self.logger.info(f"Repo {repo} with {set(versions.keys())} versions") for version, instances in versions.items(): @@ -80,7 +75,7 @@ def generate(self): repository=repo, version=version, setup_ref_instance=instances[0], - specifications=MAP_VERSION_TO_INSTALL[repo][version], + specifications=MAP_REPO_VERSION_TO_SPECS[repo][version], ) def generate_base(self, version: str) -> str: diff --git a/python/swe/dockerfiles/templates/Dockerfile.swe b/python/swe/dockerfiles/templates/Dockerfile.swe index 5bf9afc1a4a..cc8888204e7 100644 --- a/python/swe/dockerfiles/templates/Dockerfile.swe +++ b/python/swe/dockerfiles/templates/Dockerfile.swe @@ -18,8 +18,6 @@ RUN git fetch --depth 1 origin {{ basecommit}} # Checkout to base commit RUN git checkout {{ basecommit}} -WORKDIR /home/user - RUN mkdir -p /home/user/.composio/tmp COPY deeplake /home/user/.composio/tmp/{{ reponame }} COPY fqdn_cache.json /home/user/.composio/tmp/{{ reponame }} @@ -40,6 +38,8 @@ RUN /home/user/.dev/bin/python -m pip install -r _requirements.txt || echo "$?" {% endif -%} +USER root + {% if preinstall -%} # Pre install {% for cmd in preinstall -%} diff --git a/python/swe/setup.py b/python/swe/setup.py index f4858e5dcdb..77c4d5229ef 100644 --- a/python/swe/setup.py +++ b/python/swe/setup.py @@ -63,7 +63,7 @@ def scan_for_package_data( include_package_data=True, install_requires=[ "pydantic>=2.7.4", - "swebench==2.0.13", + "swebench==2.1.0", "datasets>=2.20.0", "gitpython>=3.1.43", "composio_core>=0.5.30,<=0.5.31", @@ -71,4 +71,12 @@ def scan_for_package_data( "tqdm==4.66.4", "rich", ], + extras_require={ + "langgraph": [ + "langchain-aws==0.1.17", + "langgraph>=0.2.16", + "composio_langgraph>=0.5.13", + "python-dotenv==1.0.1", + ] + }, ) diff --git a/python/swe/swekit/benchmark/run_evaluation.py b/python/swe/swekit/benchmark/run_evaluation.py index d8c1eae7c55..10afa9cbaa3 100644 --- a/python/swe/swekit/benchmark/run_evaluation.py +++ b/python/swe/swekit/benchmark/run_evaluation.py @@ -2,7 +2,7 @@ import json import os import random -import time +import traceback import typing as t from pathlib import Path @@ -10,11 +10,9 @@ from swebench.harness.test_spec import make_test_spec from tqdm import tqdm -from composio import Action, WorkspaceConfigType, WorkspaceFactory, WorkspaceType +from composio import WorkspaceConfigType, WorkspaceFactory, WorkspaceType from composio.utils.logging import WithLogger -from composio_crewai import ComposioToolSet - from swekit.benchmark.utils import ( build_issue_description, get_issues_dataset, @@ -41,6 +39,10 @@ def _get_logs_dir() -> Path: class EvaluationConfig(BaseModel): """Benchmark evaluation config.""" + dataset_name: str = Field( + default="princeton-nlp/SWE-bench_Verified", + description="dataset name", + ) test_range: str = Field( default="20:30", description="slice for the test split range", @@ -86,9 +88,11 @@ def __init__(self, config: EvaluationConfig): super().__init__() self.issues = get_issues_dataset( + dataset_name=config.dataset_name, test_split=config.test_range, test_instance_ids=config.test_instance_ids, ) + self.dataset_name = config.dataset_name self.dry_run = config.dry_run self.include_hints = config.include_hints self.logs_dir = os.path.expanduser(config.logs_dir) @@ -122,40 +126,6 @@ def get_issue_config(self, issue) -> IssueConfig: install_repo_script=test_spec.install_repo_script, ) - def get_patch_for_issue(self, workspace_id: str, issue): - composio_toolset = ComposioToolSet(workspace_id=workspace_id) - self.logger.info( - f"Agent run finished, getting patch for issue: {issue['instance_id']}" - ) - get_patch_resp = composio_toolset.execute_action( - action=Action.FILETOOL_GIT_PATCH, - params={}, - ) - self.logger.info(f"Get patch response: {get_patch_resp}") - if not get_patch_resp.get("successfull", False): - error_message = get_patch_resp.get("error") - if error_message: - raise Exception(f"Error in get_patch: {error_message}") - else: - raise Exception("Unknown error occurred in get_patch") - - patch_data = get_patch_resp.get("data", {}) - if not patch_data: - raise Exception("No data found in the patch response") - - patch = patch_data.get("patch") - if not patch: - error = patch_data.get("error") - if error: - self.logger.error(f"Error in patch data: {error}") - return None - else: - self.logger.error("No patch found in the response data") - return None - - self.logger.info(f"Final Patch: {patch}") - return patch - def save_agent_run(self, issue_config, issue_patch): Path(str(self.logs_dir)).mkdir(parents=True, exist_ok=True) task_output_log = f"{self.logs_dir}/agent_logs_{issue_config.issue_id}.json" @@ -214,12 +184,13 @@ def run(self, agent_func: t.Callable): return for count, issue in tqdm( # type: ignore - iterable=enumerate(self.issues, 1), + iterable=enumerate(list(self.issues), 1), total=len(self.issues), desc="Processing issues", ): try: - repo = issue["repo"] + issue = dict(issue) # Typecast issue to dict + repo = str(issue.get("repo")) self.logger.info( f"Processing issue: {count} with repoMap: {self.repo_to_workspace_map} " f"Repo: {repo} " @@ -229,31 +200,22 @@ def run(self, agent_func: t.Callable): image_name = self.image_name or f"composio/swe:{tag}" self.logger.info(f"Using image: {image_name}") - for attempt in range(3): - try: - workspace_ids = setup_workspace( - repo, - self.repo_to_workspace_map, - self.repo_to_image_id_map, - issue["base_commit"], - self.workspace_env, - image_name, - num_instances=self.num_instances, - ) - break # If successful, exit the retry loop - except Exception as e: - self.logger.error( - f"Error setting up workspace (attempt {attempt + 1}/3): {e}" - ) - if attempt < 2: # If this wasn't the last attempt - self.logger.info("Retrying in 5 seconds...") - time.sleep(5) # Wait for 5 seconds before retrying - else: - self.logger.error( - "All attempts to set up workspace failed. Skipping this issue." - ) - else: - continue # Skip to the next iteration of the outer loop if all attempts failed + try: + workspace_ids = setup_workspace( + repo, + self.repo_to_workspace_map, + self.repo_to_image_id_map, + issue["base_commit"], + self.workspace_env, + image_name, + num_instances=self.num_instances, + ) + except Exception as e: + self.logger.error( + f"Error setting up workspace: {e}. Traceback: {traceback.format_exc()}" + ) + continue + issue_config = self.get_issue_config(issue) self.logger.debug( "found patch-id: %s and install_commit_id: %s", @@ -261,21 +223,23 @@ def run(self, agent_func: t.Callable): issue["environment_setup_commit"], ) issue_patch = agent_func(workspace_ids, issue_config) - # issue_patch = self.get_patch_for_issue(workspace_id, issue) self.save_agent_run(issue_config, issue_patch) for workspace_id in workspace_ids: WorkspaceFactory.close(id=workspace_id) except Exception as e: - self.logger.error(f"Error processing issue {issue['instance_id']}: {e}") + self.logger.error( + f"Error processing issue {issue_config.issue_id}: {e}" + ) raise e def score_evaluation(self, run_id: str): - get_score(self.logs_dir, run_id) + get_score(self.logs_dir, run_id, self.dataset_name) def evaluate( runnable: t.Callable, + dataset_name: str = "princeton-nlp/SWE-bench_Verified", test_range: str = "20:22", workspace_type: t.Type[WorkspaceConfigType] = WorkspaceType.Docker, dry_run: bool = True, @@ -294,6 +258,7 @@ def evaluate( manager = EvaluationManager( EvaluationConfig( + dataset_name=dataset_name, test_range=test_range, dry_run=dry_run, include_hints=include_hints, diff --git a/python/swe/swekit/benchmark/utils.py b/python/swe/swekit/benchmark/utils.py index 26b69e0b113..b23fbd268b4 100644 --- a/python/swe/swekit/benchmark/utils.py +++ b/python/swe/swekit/benchmark/utils.py @@ -1,54 +1,47 @@ # pylint: disable=logging-fstring-interpolation - import concurrent.futures import datetime import glob import json -import os import typing as t import docker from datasets import Dataset, load_dataset from docker import errors as docker_errors from swebench.harness.run_evaluation import main as run_evaluation +from tenacity import retry, stop_after_attempt, wait_exponential -from composio import Action, WorkspaceFactory, WorkspaceType +from composio import Action, ComposioToolSet, WorkspaceFactory, WorkspaceType from composio.tools.env.constants import DEFAULT_IMAGE from composio.utils.logging import get as get_logger from composio.utils.url import get_api_url_base -from composio_crewai import ComposioToolSet - - -DATASET_NAME = os.environ.get("DATASET_NAME", "princeton-nlp/SWE-bench_Verified") -PATH_TESTBED = "testbed/" - logger = get_logger(name="run_evaluation") -def get_issues_dataset(test_split, test_instance_ids=[]) -> Dataset: +def get_issues_dataset(dataset_name, test_split, test_instance_ids=[]) -> Dataset: test_dataset = t.cast( Dataset, load_dataset( - DATASET_NAME, + dataset_name, split=f"test[{test_split}]", ), ) - print(f"Original test_dataset size: {len(test_dataset)}") - print(f"Number of test_instance_ids: {len(test_instance_ids)}") - print(f"First few test_instance_ids: {test_instance_ids[:5]}") + logger.info(f"Original test_dataset size: {len(test_dataset)}") + logger.info(f"Number of test_instance_ids: {len(test_instance_ids)}") + logger.info(f"First few test_instance_ids: {test_instance_ids[:5]}") if len(test_instance_ids) > 0: test_dataset = test_dataset.filter( lambda x: x["instance_id"] in test_instance_ids ) - print(f"Filtered test_dataset size: {len(test_dataset)}") + logger.info(f"Filtered test_dataset size: {len(test_dataset)}") return test_dataset -def get_score(logs_dir, run_id): +def get_score(logs_dir, run_id, dataset_name): temp = [] for files in glob.glob(f"{logs_dir}/agent_logs_*.json"): pred = json.load(open(files, "r")) @@ -63,29 +56,8 @@ def get_score(logs_dir, run_id): with open(f"{logs_dir}/predictions.json", "w") as f: json.dump(temp, f, indent=4) - # Remove dangling Docker images - import subprocess - - try: - subprocess.run( - 'docker rmi $(docker images -f "dangling=true" -q)', - shell=True, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - logger.info("Successfully removed dangling Docker images") - except subprocess.CalledProcessError as e: - logger.warning( - f"Failed to remove dangling Docker images: {e.stderr.decode().strip()}" - ) - except Exception as e: - logger.error( - f"An error occurred while trying to remove dangling Docker images: {str(e)}" - ) - run_evaluation( - dataset_name=DATASET_NAME, + dataset_name=dataset_name, split="test", instance_ids=[], predictions_path=f"{logs_dir}/predictions.json", @@ -102,79 +74,14 @@ def get_score(logs_dir, run_id): def build_issue_description(repo, hints, problem_statement, include_hints): if not problem_statement or not problem_statement.strip(): raise ValueError("problem statement is empty") - tmpl = f"""You have the repository {repo} cloned in the workspace. You are at the root of the repository. Here is the issue, that you have to solve all on your own:\n{problem_statement}. You can only make changes in the core repository {repo}.\n""" # noqa: E501 + tmpl = f"""You have the repository {repo} cloned in the workspace. You are at the root of the repository. Here is the issue, that you have to solve all on your own:\n\n{problem_statement}\n\n. You can only make changes in the core repository {repo}.\n\n""" # noqa: E501 if include_hints and hints: tmpl += f"""\n\nHere are few hints to solve the issue described in problem_statement: \n{hints}""" return tmpl -def get_workspace_from_repo_map(repo, repo_to_workspace_map, base_commit): - workspace_id = repo_to_workspace_map.get(repo) - if not workspace_id or not workspace_id.strip(): - return None - - print("Resetting repository to base commit") - composio_toolset = ComposioToolSet(workspace_id=workspace_id) - composio_toolset.execute_action( - action=Action.FILETOOL_GIT_CLONE, - params={ - "repo_name": repo, - "just_reset": True, - "commit_id": base_commit, - }, - ) - return workspace_id - - -def create_workspace_from_image(repo, repo_to_image_id_map, base_commit): - if not repo_to_image_id_map.get(repo): - logger.info("repo: %s not found in repo-to-image-map", repo) - return "" - logger.info("Using saved image") - start_time = datetime.datetime.now() - composio_toolset = ComposioToolSet(workspace_config=WorkspaceType.Host()) - workspace = WorkspaceFactory.new( - config=WorkspaceType.Docker( - image=repo_to_image_id_map[repo], - composio_api_key=composio_toolset.api_key, - composio_base_url=composio_toolset._base_url or get_api_url_base(), - github_access_token=composio_toolset._try_get_github_access_token_for_current_entity(), - ), - ) - workspace_id = workspace.id - composio_toolset.set_workspace_id( - workspace_id=workspace_id, - ) - - workspace_creation_time = datetime.datetime.now() - start_time - cd_resp = composio_toolset.execute_action( - action=Action.SHELL_EXEC_COMMAND, - params={ - "cmd": f"cd /{repo.split('/')[-1]}", - }, - ) - if isinstance(cd_resp, dict) and cd_resp.get("status") == "failure": - raise Exception(f"Error changing directory: {cd_resp['details']}") - logger.info( - "workspace is created, workspace-id is: %s, creation time: %s", - workspace_id, - workspace_creation_time, - ) - logger.info("Resetting repository to base commit") - reset_resp = composio_toolset.execute_action( - action=Action.FILETOOL_GIT_CLONE, - params={ - "repo_name": repo, - "just_reset": True, - "commit_id": base_commit, - }, - ) - if isinstance(reset_resp, dict) and not reset_resp.get("success"): - raise Exception(f"Error resetting repository: {reset_resp['error']}") - return workspace_id - - +@retry(stop=stop_after_attempt(1), wait=wait_exponential(multiplier=1, min=4, max=10)) def build_image_and_container( repo, base_commit, workspace_env=WorkspaceType.Docker, image_name=DEFAULT_IMAGE ): @@ -199,6 +106,7 @@ def build_image_and_container( ) else: raise ValueError(f"Unsupported workspace environment: {workspace_env}") + logger.info("Workspace created") workspace_creation_time = datetime.datetime.now() - start_time logger.info( "workspace is created, workspace-id is: %s, creation time: %s", @@ -322,6 +230,7 @@ def check_and_pull_image(image_name): if __name__ == "__main__": get_score( - logs_dir="/Users/shrey/.composio_coder/logs/17266821626867/", + logs_dir="/Users/shrey/.composio_coder/logs/17286398988283/", run_id="langgraph_agent_temp", + dataset_name="princeton-nlp/SWE-bench_Verified", ) diff --git a/python/swe/swekit/config/constants.py b/python/swe/swekit/config/constants.py index f2cb1a8535b..4d6b9f532f9 100644 --- a/python/swe/swekit/config/constants.py +++ b/python/swe/swekit/config/constants.py @@ -12,3 +12,18 @@ KEY_MODEL_ENV = "model_env" MODEL_ENV_AZURE = "azure" MODEL_ENV_OPENAI = "openai" + +MAP_REPO_TO_TEST_CMD = { + "django/django": "./tests/runtests.py --verbosity 2 --settings=test_sqlite --parallel 1", + "sympy/sympy": "PYTHONWARNINGS='ignore::UserWarning,ignore::SyntaxWarning' bin/test -C --verbose sympy", + "pallets/flask": "pytest -rA tests", + "astropy/astropy": "pytest -rA astropy", + "matplotlib/matplotlib": "pytest -rA lib/matplotlib/tests", + "pylint/pylint": "pytest -rA tests", + "pytest/pytest": "pytest -rA testing", + "psf/requests": "pytest -rA test_requests.py", + "scikit-learn/scikit-learn": "pytest -rA sklearn", + "mwaskom/seaborn": "pytest --no-header -rA tests", + "sphinx-doc/sphinx": "tox --current-env -epy39 -v -- tests", + "pydata/xarray": "pytest -rA xarray/tests/", +} diff --git a/python/swe/tests/test_benchmark.py b/python/swe/tests/test_benchmark.py new file mode 100644 index 00000000000..52ac88e112c --- /dev/null +++ b/python/swe/tests/test_benchmark.py @@ -0,0 +1,117 @@ +import logging +import os +from unittest.mock import MagicMock, patch + +import docker +import pytest +from datasets import Dataset + +from composio import Action, ComposioToolSet, WorkspaceType + +from swekit.benchmark.utils import ( + build_issue_description, + get_issues_dataset, + setup_workspace, +) + + +logger = logging.getLogger(__name__) + + +@pytest.fixture +def mock_load_dataset(): + with patch("swekit.benchmark.utils.load_dataset") as mock: + yield mock + + +class TestIntegration: + image_name = "composio/swe:testing" + + @classmethod + def setup_class(cls): + client = docker.from_env() + dockerfile_path = os.path.join(os.path.dirname(__file__), "test_docker") + logger.info(f"Building Docker image from path: {dockerfile_path}") + image, build_logs = client.images.build( + path=dockerfile_path, tag=cls.image_name, rm=True + ) + logger.info("Docker image built successfully.") + + def test_setup_workspace(self): + num_instances = 1 + workspace_ids = setup_workspace( + repo="pallets/flask", + repo_to_workspace_map={}, + repo_to_image_id_map={}, + base_commit="7ee9ceb71e868944a46e1ff00b506772a53a4f1d", + workspace_env=WorkspaceType.Docker, + image_name=self.image_name, + num_instances=num_instances, + ) + assert len(workspace_ids) == num_instances + composio_toolset = ComposioToolSet() + composio_toolset.set_workspace_id(workspace_id=workspace_ids[0]) + pwd_resp = composio_toolset.execute_action( + action=Action.FILETOOL_CHANGE_WORKING_DIRECTORY, params={"path": "."} + ) + assert pwd_resp["successful"] + assert pwd_resp["data"]["current_working_directory"] == "/home/user/flask" + + def test_get_issues_dataset(self, mock_load_dataset): + mock_dataset = MagicMock(spec=Dataset) + mock_dataset.__len__.return_value = 100 + mock_load_dataset.return_value = mock_dataset + + # Test without test_instance_ids + result = get_issues_dataset( + "princeton-nlp/SWE-bench_Verified", test_split="0:100" + ) + assert len(result) == 100 + mock_load_dataset.assert_called_once_with( + "princeton-nlp/SWE-bench_Verified", split="test[0:100]" + ) + + # Test with test_instance_ids + mock_dataset.filter.return_value = MagicMock(spec=Dataset) + mock_dataset.filter.return_value.__len__.return_value = 50 + result = get_issues_dataset( + "princeton-nlp/SWE-bench_Verified", + test_split="0:100", + test_instance_ids=["id1", "id2"], + ) + assert len(result) == 50 + mock_dataset.filter.assert_called_once() + + def test_build_issue_description(self): + repo = "test/repo" + problem_statement = "Fix the bug" + hints = "Check the loop" + + # Test without hints + description = build_issue_description( + repo, hints, problem_statement, include_hints=False + ) + assert repo in description + assert problem_statement in description + assert hints not in description + + # Test with hints + description = build_issue_description( + repo, hints, problem_statement, include_hints=True + ) + assert repo in description + assert problem_statement in description + assert hints in description + + # Test with empty problem statement + with pytest.raises(ValueError): + build_issue_description(repo, hints, "", include_hints=True) + + @classmethod + def teardown_class(cls): + client = docker.from_env() + try: + client.images.remove(cls.image_name, force=True) + print("Docker image removed successfully.") + except Exception as e: + print(f"Error removing Docker image: {e}") diff --git a/python/swe/tests/test_cli.py b/python/swe/tests/test_cli.py deleted file mode 100644 index 0094ec7d07c..00000000000 --- a/python/swe/tests/test_cli.py +++ /dev/null @@ -1,169 +0,0 @@ -"""Test the CLI.""" - -import traceback -import unittest -from typing import Any, Dict, List, Optional -from unittest.mock import patch - -import click.testing -from click.testing import CliRunner -from langchain_core.callbacks.manager import CallbackManagerForLLMRun -from langchain_core.language_models.llms import LLM - -from swekit.cli import swekit -from swekit.config.context import Context, set_context - - -# pylint: disable=unused-argument - - -class FakeListLLM(LLM): - """Fake LLM for testing that outputs elements of a list.""" - - responses: List[str] - i: int = -1 - - def _call( - self, - prompt: str, - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> str: - """Increment counter, and then return response in that index.""" - self.i += 1 - if self.i >= len(self.responses): - return "" - print(f"=== Mock Response #{self.i} ===") # noqa: T201 - print(self.responses[self.i]) # noqa: T201 - return self.responses[self.i] - - def get_num_tokens(self, text: str) -> int: - """Return number of tokens in text.""" - return len(text.split()) - - async def _acall(self, *args: Any, **kwargs: Any) -> str: - return self._call(*args, **kwargs) - - @property - def _identifying_params(self) -> Dict[str, Any]: - return {} - - @property - def _llm_type(self) -> str: - """Return type of llm.""" - return "fake_list" - - -class TestCLI(unittest.TestCase): - def setUp(self): - self.runner = CliRunner() - - def handle_exception(self, result: click.testing.Result): - if result.exception: - # Prepare the exception message - exception_message = f"An exception occurred: {result.exception}\n" - # Extract traceback details - if result.exc_info: - tb = traceback.format_exception(*result.exc_info) - exception_message += "".join(tb) - - # Print the exception details to the console (optional, for debugging) - print(exception_message) - - # Fail the test with the exception details - self.fail(exception_message) - - def test_openai_setup(self): - """Test the openai setup command.""" - with self.runner.isolated_filesystem(): - result = self.runner.invoke(swekit, ["setup"], input="openai\napi_key\n") - self.handle_exception(result) - self.assertIn("Model configuration saved", result.output) - self.assertEqual(result.exit_code, 0) - - def test_azure_setup(self): - """Test the azure setup command.""" - with self.runner.isolated_filesystem(): - result = self.runner.invoke( - swekit, ["setup"], input="azure\napi_key\nend_point_url\n" - ) - self.handle_exception(result) - self.assertIn("Model configuration saved", result.output) - self.assertEqual(result.exit_code, 0) - - def test_add_issue(self): - """Test the add_issue command.""" - with self.runner.isolated_filesystem(): - result = self.runner.invoke( - swekit, - ["add_issue"], - input="repo_name\nissue_id\nbase_commit_id\nissue_description\n", - env={"GITHUB_ACCESS_TOKEN": "DEFAULT-TOKEN"}, - ) - self.handle_exception(result) - self.assertEqual(result.exit_code, 0) - self.assertIn("🍀 Issue configuration saved\n", result.output) - - def test_solve_openai(self): - """Test the solve command.""" - with self.runner.isolated_filesystem(): - # Assuming 'set_context' is a method to set the context directly - - issue_config = { - "repo_name": "test_repo", - "issue_id": "123", - "base_commit_id": "abc", - "issue_desc": "Fix bug", - } - model_env_config = {"api_key": "test-api-key", "model_env": "openai"} - ctx = Context() - ctx.issue_config = issue_config - ctx.model_env = model_env_config - set_context(ctx) # Set the context directly without file I/O - - with patch("composio_coders.swe.CoderAgent.get_llm") as mock_get_llm: - mock_get_llm.return_value = FakeListLLM(responses=["Fake Response"]) - result = self.runner.invoke(swekit, ["solve"]) - self.handle_exception(result) - mock_get_llm.assert_called_once() - self.assertEqual(result.exit_code, 0) - - def test_solve_azure(self): - """Test the solve command.""" - with self.runner.isolated_filesystem(): - # Assuming 'set_context' is a method to set the context directly - - issue_config = { - "repo_name": "test_repo", - "issue_id": "123", - "base_commit_id": "abc", - "issue_desc": "Fix bug", - } - model_env_config = { - "api_key": "test-api-key", - "model_env": "azure", - "azure_endpoint": "https://abc.com", - } - ctx = Context() - ctx.issue_config = issue_config - ctx.model_env = model_env_config - set_context(ctx) # Set the context directly without file I/O - - with patch("composio_coders.swe.CoderAgent.get_llm") as mock_get_llm: - mock_get_llm.return_value = FakeListLLM(responses=["Fake Response"]) - result = self.runner.invoke(swekit, ["solve"]) - self.handle_exception(result) - mock_get_llm.assert_called_once() - self.assertEqual(result.exit_code, 0) - - def test_show_workflow(self): - """Test the show_workflow command.""" - result = self.runner.invoke(swekit, ["workflow"]) - self.handle_exception(result) - self.assertIn("Workflow:", result.output) - self.assertEqual(result.exit_code, 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/python/swe/tests/test_docker/Dockerfile b/python/swe/tests/test_docker/Dockerfile new file mode 100644 index 00000000000..9bcf809ecb0 --- /dev/null +++ b/python/swe/tests/test_docker/Dockerfile @@ -0,0 +1,64 @@ +FROM composio/composio:latest + +# Install dependencies +RUN apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \ + libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \ + xz-utils tk-dev libffi-dev liblzma-dev + +USER user + +# Go to user dir +WORKDIR /home/user + +# Install pyenv +RUN curl https://pyenv.run | bash +# Go to user dir +WORKDIR /home/user + +# Install required python version +RUN /home/user/.pyenv/bin/pyenv install 3.11.0 + +# Create development environment for agent +RUN /home/user/.pyenv/versions/3.11.0/bin/python -m venv /home/user/.dev/ + +# Upgrade packages +RUN /home/user/.dev/bin/python -m pip install --upgrade pip setuptools wheel \ + --trusted-host pypi.org \ + --trusted-host pypi.python.org \ + --trusted-host files.pythonhosted.org + +# Go to user dir +WORKDIR /home/user + +# Clone github repository +RUN git config --global http.postBuffer 157286400 && git config --global --add safe.directory /home/user/flask && git clone --depth 1 https://github.com/pallets/flask + +# Set repository as workdir +WORKDIR /home/user/flask + +# Fetch the base commit +RUN git fetch --depth 1 origin 7ee9ceb71e868944a46e1ff00b506772a53a4f1d + +# Checkout to base commit +RUN git checkout 7ee9ceb71e868944a46e1ff00b506772a53a4f1d + +WORKDIR /home/user + +# Install dependencies +RUN /home/user/.dev/bin/python -m pip install "click==8.1.3" "itsdangerous==2.1.2" "Jinja2==3.1.2" "MarkupSafe==2.1.1" "Werkzeug==2.3.7" || echo "$?" + +# Install dependencies +COPY requirements.txt _requirements.txt + +# Install from requirements.txt +RUN /home/user/.dev/bin/python -m pip install -r _requirements.txt || echo "$?" + +WORKDIR /home/user/flask + +# Install package +RUN /home/user/.dev/bin/python -m pip install -e . || echo "$?" + +ENV HOME=/home/user/ + +# Switch to root +USER root \ No newline at end of file diff --git a/python/swe/tests/test_docker/requirements.txt b/python/swe/tests/test_docker/requirements.txt new file mode 100644 index 00000000000..81ff3903080 --- /dev/null +++ b/python/swe/tests/test_docker/requirements.txt @@ -0,0 +1,65 @@ +alabaster==0.7.13 +babel==2.12.1 +certifi==2022.12.7 +charset-normalizer==3.1.0 +docutils==0.17.1 +idna==3.4 +imagesize==1.4.1 +jinja2==3.1.2 +markupsafe==2.1.2 +packaging==23.0 +pallets-sphinx-themes==2.0.3 +pygments==2.15.0 +requests==2.28.2 +snowballstemmer==2.2.0 +sphinx==4.5.0 +sphinx-issues==3.0.1 +sphinx-tabs==3.3.1 +sphinxcontrib-applehelp==1.0.4 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==2.0.1 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-log-cabinet==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.5 +urllib3==1.26.15 + +asgiref==3.6.0 +iniconfig==2.0.0 +packaging==23.0 +pluggy==1.0.0 +pytest==7.3.0 +python-dotenv==1.0.0 ; python_version >= "3.8" + +cffi==1.15.1 +cryptography==40.0.1 +mypy==1.2.0 +mypy-extensions==1.0.0 +pycparser==2.21 +types-contextvars==2.4.7.2 +types-dataclasses==0.6.6 +types-setuptools==67.6.0.7 +typing-extensions==4.5.0 + +build==0.10.0 +cachetools==5.3.0 +cfgv==3.3.1 +chardet==5.1.0 +click==8.1.3 +colorama==0.4.6 +distlib==0.3.6 +filelock==3.11.0 +identify==2.5.22 +nodeenv==1.7.0 +pip-compile-multi==2.6.2 +pip-tools==6.13.0 +platformdirs==3.2.0 +pre-commit==3.2.2 +pyproject-api==1.5.1 +pyproject-hooks==1.0.0 +pyyaml==6.0 +toposort==1.10 +tox==4.4.11 +virtualenv==20.21.0 +wheel==0.40.0 + diff --git a/python/tox.ini b/python/tox.ini index 5a8494678e5..a0e97b89229 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -93,6 +93,10 @@ deps = pytest-codecov==0.5.1 typing_extensions>=4.10.0 tree_sitter==0.21.3# codeanalysis + python-dotenv==1.0.1 + ; composio_langgraph==0.5.13 + langgraph==0.2.16 + langchain-aws==0.1.17 deeplake # codeanalysis git+https://github.com/DataDog/jedi.git@92d0c807b0dcd115b1ffd0a4ed21e44db127c2fb#egg=jedi # codeanalysis libcst # codeanalysis @@ -104,13 +108,24 @@ deps = docker>=7.1.0 # Docker workspace gql # FlyIO workspace requests_toolbelt # FlyIO workspace + + ; SWEKit + pydantic>=2.7.4 + swebench==2.1.0 + datasets>=2.20.0 + gitpython>=3.1.43 + unidiff==0.7.5 + tqdm==4.66.4 commands = ; Setup tests composio apps update + ; Install swekit + pip install -e swe/ --no-deps + ; TODO: Extract plugin tests separately ; Installing separately because of the dependency conflicts - pytest -vvv -rfE --doctest-modules composio/ tests/ --junitxml=junit.xml --cov=composio --cov=examples --cov-report=html --cov-report=xml --cov-report=term --cov-report=term-missing --cov-config=.coveragerc {posargs} + pytest -vvv -rfE --doctest-modules composio/ tests/ swe/tests --junitxml=junit.xml --cov=composio --cov=examples --cov=swe --cov-report=html --cov-report=xml --cov-report=term --cov-report=term-missing --cov-config=.coveragerc {posargs} ; pip3 install plugins/autogen ; pip3 install plugins/claude