diff --git a/apps/agents/poetry.lock b/apps/agents/poetry.lock index b960a8b4..d6afd7ca 100644 --- a/apps/agents/poetry.lock +++ b/apps/agents/poetry.lock @@ -86,6 +86,87 @@ files = [ {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.4.1" @@ -212,7 +293,7 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["main"] -markers = "python_version <= \"3.11\" and platform_system == \"Windows\" or python_version >= \"3.12\" and platform_system == \"Windows\"" +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -342,6 +423,22 @@ Werkzeug = ">=3.1" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "griffe" +version = "1.6.2" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "griffe-1.6.2-py3-none-any.whl", hash = "sha256:6399f7e663150e4278a312a8e8a14d2f3d7bd86e2ef2f8056a1058e38579c2ee"}, + {file = "griffe-1.6.2.tar.gz", hash = "sha256:3a46fa7bd83280909b63c12b9a975732a927dd97809efe5b7972290b606c5d91"}, +] + +[package.dependencies] +colorama = ">=0.4" + [[package]] name = "gunicorn" version = "23.0.0" @@ -912,15 +1009,15 @@ files = [ [[package]] name = "openai" -version = "1.59.7" +version = "1.68.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" groups = ["main"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "openai-1.59.7-py3-none-any.whl", hash = "sha256:cfa806556226fa96df7380ab2e29814181d56fea44738c2b0e581b462c268692"}, - {file = "openai-1.59.7.tar.gz", hash = "sha256:043603def78c00befb857df9f0a16ee76a3af5984ba40cb7ee5e2f40db4646bf"}, + {file = "openai-1.68.0-py3-none-any.whl", hash = "sha256:20e279b0f3a78cb4a95f3eab2a180f3ee30c6a196aeebd6bf642a4f88ab85ee1"}, + {file = "openai-1.68.0.tar.gz", hash = "sha256:c570c06c9ba10f98b891ac30a3dd7b5c89ed48094c711c7a3f35fb5ade6c0757"}, ] [package.dependencies] @@ -928,8 +1025,10 @@ anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" jiter = ">=0.4.0,<1" +numpy = ">=2.0.2" pydantic = ">=1.9.0,<3" sniffio = "*" +sounddevice = ">=0.5.1" tqdm = ">4" typing-extensions = ">=4.11,<5" @@ -937,6 +1036,27 @@ typing-extensions = ">=4.11,<5" datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] realtime = ["websockets (>=13,<15)"] +[[package]] +name = "openai-agents" +version = "0.0.4" +description = "OpenAI Agents SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "openai_agents-0.0.4-py3-none-any.whl", hash = "sha256:5577c3ee994fe0bd200d7283e4f7a614b3af19afeebcfb07b6ca6039a8a50a5c"}, + {file = "openai_agents-0.0.4.tar.gz", hash = "sha256:297e8d5faeca753e1b303d860b7ac94d03a7e10382be738163dc6a10a3b7cc1c"}, +] + +[package.dependencies] +griffe = ">=1.5.6,<2" +openai = ">=1.66.2" +pydantic = ">=2.10,<3" +requests = ">=2.0,<3" +types-requests = ">=2.0,<3" +typing-extensions = ">=4.12.2,<5" + [[package]] name = "openpyxl" version = "3.1.5" @@ -1054,6 +1174,19 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.9.2)"] +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pydantic" version = "2.10.5" @@ -1406,6 +1539,28 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sounddevice" +version = "0.5.1" +description = "Play and Record Sound with Python" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c"}, + {file = "sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031"}, + {file = "sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055"}, + {file = "sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1"}, + {file = "sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041"}, +] + +[package.dependencies] +CFFI = ">=1.0" + +[package.extras] +numpy = ["NumPy"] + [[package]] name = "soupsieve" version = "2.6" @@ -1458,6 +1613,22 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "types-requests" +version = "2.32.0.20250306" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b"}, + {file = "types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1"}, +] + +[package.dependencies] +urllib3 = ">=2" + [[package]] name = "typing-extensions" version = "4.12.2" @@ -1655,4 +1826,4 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "ef220af6b184e760ccc9b45e26ec9f58a54cb9327c562a612798433a7f9c08e4" +content-hash = "ee79f77afa3bbdb99810fd4a1a9cae366a307e2af74fd70c6d3376c6fdab61b3" diff --git a/apps/agents/pyproject.toml b/apps/agents/pyproject.toml index 872c69a5..a1206c53 100644 --- a/apps/agents/pyproject.toml +++ b/apps/agents/pyproject.toml @@ -41,6 +41,7 @@ mypy-extensions = "^1.0.0" nest-asyncio = "^1.6.0" numpy = "^2.1.2" openai = "^1.52.2" +openai-agents = "^0.0.4" openpyxl = "^3.1.5" pandas = "^2.2.3" pydantic = "^2.9.2" diff --git a/apps/agents/requirements.txt b/apps/agents/requirements.txt index 54a25f05..26c51d26 100644 --- a/apps/agents/requirements.txt +++ b/apps/agents/requirements.txt @@ -43,6 +43,7 @@ mypy-extensions==1.0.0 nest-asyncio==1.6.0 numpy==2.2.1 openai==1.59.7 +openai-agents==0.0.4 openpyxl==3.1.5 packaging==24.2 pandas==2.2.3 diff --git a/apps/agents/src/app/main.py b/apps/agents/src/app/main.py index 86cf9201..f78b35b6 100644 --- a/apps/agents/src/app/main.py +++ b/apps/agents/src/app/main.py @@ -37,67 +37,49 @@ def require_api_key(f): @app.route("/chat", methods=["POST"]) @require_api_key def chat(): - print('='*200) logger.info('='*200) + logger.info(f"{'*'*50}Running server mode{'*'*50}") try: data = request.get_json() - print('Complete request:') - logger.info('Complete request') - print(data) + logger.info('Complete request:') logger.info(data) - - print('-'*200) logger.info('-'*200) start_time = datetime.now() config = read_json_from_file("./configs/default_config.json") + logger.info('Beginning turn') resp_messages, resp_tokens_used, resp_state = run_turn( messages=data.get("messages", []), start_agent_name=data.get("startAgent", ""), agent_configs=data.get("agents", []), tool_configs=data.get("tools", []), - localize_history=config.get("localize_history", True), - return_diff_messages=config.get("return_diff_messages", True), - prompt_configs=data.get("prompts", []), start_turn_with_start_agent=config.get("start_turn_with_start_agent", False), - children_aware_of_parent=config.get("children_aware_of_parent", False), - parent_has_child_history=config.get("parent_has_child_history", True), state=data.get("state", {}), - additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL], - max_messages_per_turn=config.get("max_messages_per_turn", 2), - max_messages_per_error_escalation_turn=config.get("max_messages_per_error_escalation_turn", 2), - escalate_errors=config.get("escalate_errors", True), - max_overall_turns=config.get("max_overall_turns", 10) + additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL] ) - print('-'*200) logger.info('-'*200) - + logger.info('Raw output:') + logger.info((resp_messages, resp_tokens_used, resp_state)) + out = { "messages": resp_messages, "tokens_used": resp_tokens_used, "state": resp_state, } - print("Output: ") - logger.info(f"Output: ") + logger.info("Output:") for k, v in out.items(): - print(f"{k}: {v}") - print('*'*200) logger.info(f"{k}: {v}") logger.info('*'*200) - print("Processing time:") - print('='*200) logger.info('='*200) - print(f"Processing time: {datetime.now() - start_time}") logger.info(f"Processing time: {datetime.now() - start_time}") return jsonify(out) except Exception as e: - print(e) logger.error(f"Error: {e}") return jsonify({"error": str(e)}), 500 diff --git a/apps/agents/src/graph/core.py b/apps/agents/src/graph/core.py index 67eb618f..c1f24102 100644 --- a/apps/agents/src/graph/core.py +++ b/apps/agents/src/graph/core.py @@ -1,23 +1,20 @@ from copy import deepcopy import logging -from .types import AgentRole from .helpers.access import ( get_agent_by_name, - get_external_tools, pop_agent_config_by_type + get_external_tools, ) from .helpers.state import ( - add_recent_messages_to_history, construct_state_from_response, reset_current_turn, reset_current_turn_agent_history -) -from .helpers.instructions import ( - get_universal_system_message + construct_state_from_response ) from .helpers.control import get_latest_assistant_msg, get_latest_non_assistant_messages, get_last_agent_name from .swarm_wrapper import run as swarm_run, create_response, get_agents +from src.utils.common import common_logger as logger # Create a dedicated logger for swarm wrapper -logger = logging.getLogger("graph") logger.setLevel(logging.INFO) +print("Logger level set to INFO") def order_messages(messages): @@ -52,7 +49,7 @@ def clean_up_history(agent_data): data["history"] = order_messages(data["history"]) return agent_data -def create_final_response(response, turn_messages, messages, tokens_used, all_agents, return_diff_messages): +def create_final_response(response, turn_messages, tokens_used, all_agents): """ Constructs the final response data (messages, tokens_used, updated state) that a caller would need. """ @@ -61,7 +58,7 @@ def create_final_response(response, turn_messages, messages, tokens_used, all_ag response.messages = [] # Assign the appropriate messages to the response - response.messages = turn_messages if return_diff_messages else messages + turn_messages + response.messages = turn_messages # Ensure tokens_used is a valid dictionary if not isinstance(tokens_used, dict): @@ -83,17 +80,14 @@ def create_final_response(response, turn_messages, messages, tokens_used, all_ag def run_turn( - messages, start_agent_name, agent_configs, tool_configs, available_tool_mappings={}, - localize_history=True, return_diff_messages=True, prompt_configs=[], start_turn_with_start_agent=False, - children_aware_of_parent=False, parent_has_child_history=True, state={}, additional_tool_configs=[], - error_tool_call=True, max_messages_per_turn=10, max_messages_per_error_escalation_turn=4, - escalate_errors=True, max_overall_turns=10 + messages, start_agent_name, agent_configs, tool_configs, start_turn_with_start_agent, state={}, additional_tool_configs=[] ): """ Coordinates a single 'turn' of conversation or processing among agents. Includes validation, agent setup, optional greeting logic, error handling, and post-processing steps. """ logger.info("Running stateless turn") + print("Running stateless turn") # Sort messages by the specified ordering #messages = order_messages(messages) @@ -107,17 +101,11 @@ def run_turn( # Initialize tokens_used as a dictionary tokens_used = {"total": 0, "prompt": 0, "completion": 0} - # Extract special agent configs - post_processing_agent_config, agent_configs = pop_agent_config_by_type(agent_configs, AgentRole.POST_PROCESSING.value) - guardrails_agent_config, agent_configs = pop_agent_config_by_type(agent_configs, AgentRole.GUARDRAILS.value) - agent_data = state.get("agent_data", []) - universal_sys_msg = "" # If not a greeting turn, localize the last user or system messages if not greeting_turn: latest_assistant_msg = get_latest_assistant_msg(messages) - universal_sys_msg = get_universal_system_message(messages) latest_non_assistant_msgs = get_latest_non_assistant_messages(messages) msg_type = latest_non_assistant_msgs[-1]["role"] @@ -130,19 +118,6 @@ def run_turn( latest_assistant_msg=latest_assistant_msg, start_turn_with_start_agent=start_turn_with_start_agent ) - - # Localize history - if msg_type == "user": - messages = reset_current_turn(messages) - agent_data = reset_current_turn_agent_history(agent_data, [last_agent_name]) - #agent_data = clean_up_history(agent_data) - agent_data = add_recent_messages_to_history( - recent_messages=latest_non_assistant_msgs, - last_agent_name=last_agent_name, - agent_data=agent_data, - messages=messages, - parent_has_child_history=parent_has_child_history - ) else: # For a greeting turn, we assume the last agent is the start_agent_name last_agent_name = start_agent_name @@ -151,39 +126,34 @@ def run_turn( # Initialize all agents logger.info("Initializing agents") - all_agents, new_agents = get_agents( + print("Initializing agents") + new_agents = get_agents( agent_configs=agent_configs, - tool_configs=tool_configs, - available_tool_mappings=available_tool_mappings, - agent_data=agent_data, - localize_history=localize_history, - start_turn_with_start_agent=start_turn_with_start_agent, - children_aware_of_parent=children_aware_of_parent, - universal_sys_msg=universal_sys_msg + tool_configs=tool_configs ) # Prepare escalation agent - - # Get the last agent and validate - last_agent = get_agent_by_name(last_agent_name, all_agents) last_new_agent = get_agent_by_name(last_agent_name, new_agents) # Gather external tools for Swarm external_tools = get_external_tools(tool_configs) logger.info(f"Found {len(external_tools)} external tools") + print(f"Found {len(external_tools)} external tools") # If no validation error yet, proceed with the main run + logger.info("Running swarm run") + print("Running swarm run") + response = swarm_run( agent=last_new_agent, messages=messages, - execute_tools=True, external_tools=external_tools, - localize_history=localize_history, - parent_has_child_history=parent_has_child_history, - max_messages_per_turn=max_messages_per_turn, tokens_used=tokens_used ) + logger.info("Swarm run completed") + print("Swarm run completed") + # Initialize response.messages if it doesn't exist if not hasattr(response, 'messages'): response.messages = [] @@ -202,7 +172,7 @@ def run_turn( standard_message = { "role": raw_item.role if hasattr(raw_item, 'role') else "assistant", "content": content, - "sender": last_agent.name, + "sender": last_new_agent.name, "created_at": None, "response_type": "internal" } @@ -210,6 +180,9 @@ def run_turn( # Add the converted message to response messages response.messages.append(standard_message) + logger.info("Converted message added to response messages") + print("Converted message added to response messages") + # Use a dictionary for tokens_used instead of a hard-coded integer tokens_used = {"total": 100, "prompt": 50, "completion": 50} # Dummy values as placeholders @@ -217,11 +190,13 @@ def run_turn( if hasattr(response, 'messages') and isinstance(response.messages, list): turn_messages.extend(response.messages) - logger.info(f"Completed run of agent: {last_agent.name}") + logger.info(f"Completed run of agent: {last_new_agent.name}") + print(f"Completed run of agent: {last_new_agent.name}") # Otherwise, duplicate the last response as external logger.info("No post-processing agent found. Duplicating last response and setting to external.") + print("No post-processing agent found. Duplicating last response and setting to external.") if turn_messages: duplicate_msg = deepcopy(turn_messages[-1]) duplicate_msg["response_type"] = "external" @@ -234,7 +209,7 @@ def run_turn( response = create_response( messages=[duplicate_msg], tokens_used=tokens_used, - agent=last_agent, + agent=last_new_agent, error_msg='' ) @@ -243,11 +218,11 @@ def run_turn( turn_messages.extend(response.messages) # Finalize the response + logger.info("Finalizing response") + print("Finalizing response") return create_final_response( response=response, turn_messages=turn_messages, - messages=messages, tokens_used=tokens_used, - all_agents=all_agents, - return_diff_messages=return_diff_messages + all_agents=new_agents ) diff --git a/apps/agents/src/graph/helpers/state.py b/apps/agents/src/graph/helpers/state.py index c98c0462..c1b2427c 100644 --- a/apps/agents/src/graph/helpers/state.py +++ b/apps/agents/src/graph/helpers/state.py @@ -49,13 +49,7 @@ def construct_state_from_response(response, agents): for agent in agents: agent_data.append({ "name": agent.name, - "instructions": agent.instructions, - "parent_function": agent.parent_function.__name__ if agent.parent_function else None, - "child_functions": [f.__name__ for f in agent.child_functions.values()] if agent.child_functions else [], - "internal_tools": [t.get("function").get("name") for t in agent.internal_tools] if agent.internal_tools else [], - "external_tools": [t.get("function").get("name") for t in agent.external_tools] if agent.external_tools else [], - "history": agent.history, - "most_recent_parent_name": agent.most_recent_parent.name if agent.most_recent_parent else "" + "instructions": agent.instructions }) state = { diff --git a/apps/agents/src/graph/swarm_wrapper.py b/apps/agents/src/graph/swarm_wrapper.py index 03db1eb3..161c598a 100644 --- a/apps/agents/src/graph/swarm_wrapper.py +++ b/apps/agents/src/graph/swarm_wrapper.py @@ -1,30 +1,31 @@ -from src.swarm.types import Agent as SwarmAgent, Response as SwarmResponse import logging import json # Import helper functions needed for get_agents from .helpers.access import ( - get_agent_data_by_name, get_agent_by_name, get_tool_config_by_name, + get_tool_config_by_name, get_tool_config_by_type ) -from .helpers.transfer import create_transfer_function_to_agent, create_transfer_function_to_parent_agent from .helpers.instructions import ( - add_transfer_instructions_to_child_agents, add_transfer_instructions_to_parent_agents, - add_rag_instructions_to_agent, add_universal_system_message_to_agent + add_rag_instructions_to_agent ) -from agents import Agent as NewAgent, Runner, FunctionTool, function_tool, RunContextWrapper +from agents import Agent as NewAgent, Runner, FunctionTool, RunContextWrapper # Add import for OpenAI functionality -from src.utils.common import generate_openai_output +from src.utils.common import common_logger as logger, generate_openai_output from typing import Any # Create a dedicated logger for swarm wrapper -logger = logging.getLogger("swarm_wrapper") -logger.setLevel(logging.INFO) +#logger = logging.getLogger("swarm_wrapper") +#logger.setLevel(logging.INFO) -# Re-export the types from src.swarm.types -Agent = SwarmAgent -Response = SwarmResponse +from pydantic import BaseModel +from typing import List, Optional, Dict +class NewResponse(BaseModel): + messages: List[Dict] + agent: Optional[Any] = None + tokens_used: Optional[dict] = {} + error_msg: Optional[str] = "" async def catch_all(ctx: RunContextWrapper[Any], args: str, tool_name: str, tool_config: dict) -> str: print(f"Catch all called for tool: {tool_name}") @@ -43,25 +44,22 @@ async def catch_all(ctx: RunContextWrapper[Any], args: str, tool_name: str, tool print(response_content) return(response_content) -def get_agents(agent_configs, tool_configs, localize_history, available_tool_mappings, - agent_data, start_turn_with_start_agent, children_aware_of_parent, universal_sys_msg): +def get_agents(agent_configs, tool_configs): """ Creates and initializes Agent objects based on their configurations and connections. - This function also sets up parent-child relationships, transfer instructions, and - universal system messages. """ if not isinstance(agent_configs, list): raise ValueError("Agents config is not a list in get_agents") if not isinstance(tool_configs, list): raise ValueError("Tools config is not a list in get_agents") - agents = [] new_agents = [] new_agent_to_children = {} new_agent_name_to_index = {} # Create Agent objects from config for agent_config in agent_configs: logger.debug(f"Processing config for agent: {agent_config['name']}") + print(f"Processing config for agent: {agent_config['name']}") # If hasRagSources, append the RAG tool to the agent's tools if agent_config.get("hasRagSources", False): @@ -71,11 +69,9 @@ def get_agents(agent_configs, tool_configs, localize_history, available_tool_map # Prepare tool lists for this agent external_tools = [] - candidate_parent_functions = {} - child_functions = {} logger.debug(f"Agent {agent_config['name']} has {len(agent_config['tools'])} configured tools") - print(tool_configs) + print(f"Agent {agent_config['name']} has {len(agent_config['tools'])} configured tools") new_tools = [] for tool_name in agent_config["tools"]: @@ -97,33 +93,15 @@ def get_agents(agent_configs, tool_configs, localize_history, available_tool_map ) new_tools.append(tool) logger.debug(f"Added tool {tool_name} to agent {agent_config['name']}") + print(f"Added tool {tool_name} to agent {agent_config['name']}") else: logger.warning(f"Tool {tool_name} not found in tool_configs") - - # Localize history (if applicable) - history = [] - this_agent_data = get_agent_data_by_name(agent_config["name"], agent_data) - if this_agent_data and localize_history: - history = this_agent_data.get("history", []) + print(f"WARNING: Tool {tool_name} not found in tool_configs") # Create the agent object logger.debug(f"Creating Agent object for {agent_config['name']}") + print(f"Creating Agent object for {agent_config['name']}") try: - agent = Agent( - name=agent_config["name"], - type=agent_config.get("type", "default"), - instructions=agent_config["instructions"], - description=agent_config.get("description", ""), - internal_tools=[], - external_tools=external_tools, - candidate_parent_functions=candidate_parent_functions, - child_functions=child_functions, - model=agent_config["model"], - respond_to_user=agent_config.get("respond_to_user", False), - history=history, - children_names=agent_config.get("connectedAgents", []), - most_recent_parent=None - ) new_agent = NewAgent( name=agent_config["name"], instructions=agent_config["instructions"], @@ -134,54 +112,13 @@ def get_agents(agent_configs, tool_configs, localize_history, available_tool_map new_agent_to_children[agent_config["name"]] = agent_config.get("connectedAgents", []) new_agent_name_to_index[agent_config["name"]] = len(new_agents) new_agents.append(new_agent) - agents.append(agent) logger.debug(f"Successfully created agent: {agent_config['name']}") + print(f"Successfully created agent: {agent_config['name']}") except Exception as e: logger.error(f"Failed to create agent {agent_config['name']}: {str(e)}") + print(f"ERROR: Failed to create agent {agent_config['name']}: {str(e)}") raise - # Reattach most_recent_parent if it exists - for agent in agents: - this_agent_data = get_agent_data_by_name(agent.name, agent_data) - if this_agent_data: - most_recent_parent_name = this_agent_data.get("most_recent_parent_name", "") - if most_recent_parent_name: - parent_agent = get_agent_by_name(most_recent_parent_name, agents) - if parent_agent: - agent.most_recent_parent = parent_agent - - # Attach children - logger.info("Adding children agents to parent agents") - for agent in agents: - agent.children = { - potential_child.name: potential_child - for potential_child in agents - if potential_child.name in agent.children_names - } - - # Generate transfer functions for child agents - logger.info("Generating transfer functions for transferring to children agents") - transfer_functions = { - agent.name: create_transfer_function_to_agent(agent) - for agent in agents - } - - # Add transfer functions to parent agents for each child - logger.info("Adding transfer functions for parents to transfer to children") - for agent in agents: - for child in agent.children.values(): - agent.child_functions[child.name] = transfer_functions[child.name] - - # Add parent-related instructions - logger.info("Adding child transfer-related instructions to parent agents") - for agent in agents: - if agent.children: - add_transfer_instructions_to_parent_agents(agent, agent.children, transfer_functions) - - # Finally add a universal system message to all agents - for agent in agents: - add_universal_system_message_to_agent(agent, universal_sys_msg) - for new_agent in new_agents: # Initialize the handoffs attribute if it doesn't exist if not hasattr(new_agent, 'handoffs'): @@ -189,7 +126,7 @@ def get_agents(agent_configs, tool_configs, localize_history, available_tool_map # Look up the agent's children from the old agent and create a list called handoffs in new_agent with pointers to the children in new_agents new_agent.handoffs = [new_agents[new_agent_name_to_index[child]] for child in new_agent_to_children[new_agent.name]] - return agents, new_agents + return new_agents def create_response(messages=None, tokens_used=None, agent=None, error_msg=''): @@ -210,10 +147,10 @@ def create_response(messages=None, tokens_used=None, agent=None, error_msg=''): if tokens_used is None: tokens_used = {} - return Response( + return NewResponse( messages=messages, - tokens_used=tokens_used, agent=agent, + tokens_used=tokens_used, error_msg=error_msg ) @@ -221,11 +158,7 @@ def create_response(messages=None, tokens_used=None, agent=None, error_msg=''): def run( agent, messages, - execute_tools=True, external_tools=None, - localize_history=True, - parent_has_child_history=True, - max_messages_per_turn=10, tokens_used=None ): """ @@ -245,6 +178,7 @@ def run( Response object from the Swarm client """ logger.info(f"Initializing Swarm client for agent: {agent.name}") + print(f"Initializing Swarm client for agent: {agent.name}") # Initialize default parameters if external_tools is None: @@ -271,7 +205,10 @@ def run( }) # Run the agent with the formatted messages + logger.info("Beginning Swarm run with run_sync") + print("Beginning Swarm run with run_sync") response2 = Runner.run_sync(agent, formatted_messages) logger.info(f"Completed Swarm run for agent: {agent.name}") + print(f"Completed Swarm run for agent: {agent.name}") return response2 \ No newline at end of file diff --git a/apps/agents/src/swarm/__init__.py b/apps/agents/src/swarm/__init__.py deleted file mode 100644 index a47b6210..00000000 --- a/apps/agents/src/swarm/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import Swarm -from .types import Agent, Response - -__all__ = ["Swarm", "Agent", "Response"] diff --git a/apps/agents/src/swarm/core.py b/apps/agents/src/swarm/core.py deleted file mode 100644 index 663e5894..00000000 --- a/apps/agents/src/swarm/core.py +++ /dev/null @@ -1,275 +0,0 @@ -# Standard library imports -import copy -import json -from collections import defaultdict -from typing import List, Callable, Union -from datetime import datetime - -# Package/library imports -from openai import OpenAI -import random - -# Local imports -from .util import * -from .types import ( - Agent, - AgentFunction, - ChatCompletionMessage, - ChatCompletionMessageToolCall, - Function, - Response, - Result, -) - -__CTX_VARS_NAME__ = "context_variables" - - -class Swarm: - def __init__(self, client=None): - if not client: - client = OpenAI(api_key=OPENAI_API_KEY) - self.client = client - self.history = defaultdict(lambda : []) - - def get_chat_completion( - self, - agent: Agent, - history: List, - context_variables: dict, - model_override: str, - stream: bool, - debug: bool, - temperature: float - ) -> ChatCompletionMessage: - context_variables = defaultdict(str, context_variables) - instructions = ( - agent.instructions(context_variables) - if callable(agent.instructions) - else agent.instructions - ) - messages = [{"role": "system", "content": instructions}] + history - debug_print(debug, "Getting chat completion for...:", messages) - - all_functions = list(agent.child_functions.values()) + ([agent.parent_function] if agent.parent_function else []) - all_tools = agent.external_tools + agent.internal_tools - funcs_and_tools = [function_to_json(f) for f in all_functions] + [t for t in all_tools] - # hide context_variables from model - for tool in funcs_and_tools: - params = tool["function"]["parameters"] - params["properties"].pop(__CTX_VARS_NAME__, None) - if __CTX_VARS_NAME__ in params.get("required", []): - params["required"].remove(__CTX_VARS_NAME__) - - create_params = { - "model": model_override or agent.model, - "messages": messages, - "tools": funcs_and_tools or None, - "tool_choice": agent.tool_choice, - "stream": stream, - "temperature": temperature - } - - if funcs_and_tools: - create_params["parallel_tool_calls"] = agent.parallel_tool_calls - - return self.client.chat.completions.create(**create_params) - - def handle_function_result(self, result, debug) -> Result: - # Check if result is already a Result instance - if isinstance(result, Result): - return result - - # Check if result is an Agent instance - if isinstance(result, Agent): - return Result( - value=json.dumps({"assistant": result.name}), - agent=result, - ) - - # Handle all other cases - try: - return Result(value=str(result)) - except Exception as e: - error_message = f"Failed to cast response to string: {result}. Make sure agent functions return a string or Result object. Error: {str(e)}" - debug_print(debug, error_message) - raise TypeError(error_message) - - def handle_function_calls( - self, - tool_calls: List[ChatCompletionMessageToolCall], - functions: List[AgentFunction], - context_variables: dict, - debug: bool, - ) -> Response: - function_map = {f.__name__: f for f in functions} - partial_response = Response( - messages=[], agent=None, context_variables={}) - - for tool_call in tool_calls: - name = tool_call.function.name - # handle missing tool case, skip to next tool - if name not in function_map: - debug_print(debug, f"Tool {name} not found in function map.") - partial_response.messages.append( - { - "role": "tool", - "tool_call_id": tool_call.id, - "tool_name": name, - "content": f"Error: Tool {name} not found.", - } - ) - continue - args = json.loads(tool_call.function.arguments) - debug_print( - debug, f"Processing tool call: {name} with arguments {args}") - - func = function_map[name] - # pass context_variables to agent functions - if __CTX_VARS_NAME__ in func.__code__.co_varnames: - args[__CTX_VARS_NAME__] = context_variables - raw_result = function_map[name](**args) - - result: Result = self.handle_function_result(raw_result, debug) - partial_response.messages.append( - { - "role": "tool", - "tool_call_id": tool_call.id, - "tool_name": name, - "content": result.value, - } - ) - partial_response.context_variables.update(result.context_variables) - if result.agent: - partial_response.agent = result.agent - - return partial_response - - def run( - self, - agent: Agent, - messages: List, - context_variables: dict = {}, - model_override: str = None, - stream: bool = False, - debug: bool = False, - max_messages_per_turn: int = 10, - execute_tools: bool = True, - external_tools: List[str] = [], - localize_history: bool = True, - parent_has_child_history: bool = True, - tokens_used: dict = {}, - temperature: float = 0.0 - ) -> Response: - - active_agent = agent - context_variables = copy.deepcopy(context_variables) - global_history = copy.deepcopy(messages) - init_len = len(messages) - - while len(global_history) - init_len < max_messages_per_turn and active_agent: - history = active_agent.history if localize_history else global_history - history = arrange_messages_keys_in_order(history) - - parent = active_agent.most_recent_parent - - children_names_backup, children_backup, child_functions_backup = copy.deepcopy(active_agent.children_names), copy.deepcopy(active_agent.children), copy.deepcopy(active_agent.child_functions) - - active_agent = check_and_remove_repeat_tool_call_to_child(active_agent, history) - - # get completion with current history, agent - completion = self.get_chat_completion( - agent=active_agent, - history=history, - context_variables=context_variables, - model_override=model_override, - stream=stream, - debug=debug, - temperature=temperature - ) - tokens_used = update_tokens_used(provider="openai", model=model_override or active_agent.model, tokens_used=tokens_used, completion=completion) - - # Restore children and child functions - active_agent.children_names, active_agent.children, active_agent.child_functions = children_names_backup, children_backup, child_functions_backup - - message = completion.choices[0].message - debug_print(debug, "Received completion:", message) - message.sender = active_agent.name - message_json = json.loads(message.model_dump_json()) - message_json = add_message_metadata(message_json, active_agent) - - if localize_history: - active_agent = update_histories(active_agent, message_json) - if parent and parent_has_child_history: - parent = update_histories(parent, message_json) - global_history.append(message_json) - - external_tool_calls = [] - internal_tool_calls = [] - - if message.tool_calls: - message_json["response_type"] = "internal" - for tool_call in message.tool_calls: - tool_name = tool_call.function.name - if tool_name in external_tools: - external_tool_calls.append(tool_call) - else: - internal_tool_calls.append(tool_call) - message.tool_calls = internal_tool_calls - - if not message.tool_calls or not execute_tools: - if external_tool_calls: - message.tool_calls.extend(external_tool_calls) - debug_print(debug, "Ending turn.") - break - - # handle function calls, updating context_variables, and switching agents - all_functions = list(active_agent.child_functions.values()) + ([active_agent.parent_function] if active_agent.parent_function else []) - partial_response = self.handle_function_calls( - message.tool_calls, all_functions, context_variables, debug - ) - for msg in partial_response.messages: - msg = add_message_metadata(msg, active_agent) - if localize_history: - active_agent = update_histories(active_agent, msg) - if parent and parent_has_child_history: - parent = update_histories(parent, msg) - - global_history.extend(partial_response.messages) - context_variables.update(partial_response.context_variables) - - # Parent to child transfer - if partial_response.agent: - prev_agent = active_agent - active_agent = partial_response.agent - - # Parent to child transfer - if active_agent.name in prev_agent.children_names: - active_agent.most_recent_parent = prev_agent - active_agent.parent_function = active_agent.candidate_parent_functions[active_agent.most_recent_parent.name] - if localize_history: - if not parent_has_child_history: - prev_agent.history = remove_irrelevant_messages(prev_agent.history) - new_active_agent_history = get_current_turn_messages(global_history, only_user = True) - active_agent.history.extend(new_active_agent_history) - - # Child to parent transfer - else: - assert parent == active_agent, "Parent and active agent do not match when active agent is not a child of previous agent" - child = prev_agent - if localize_history: - child.history = remove_irrelevant_messages(child.history) - - - return_messages = global_history[init_len:] - error_msg = "" - - if len(global_history) - init_len >= max_messages_per_turn: - error_msg = "Max messages per turn reached" - - return Response( - messages=return_messages, - agent=active_agent, - context_variables=context_variables, - error_msg=error_msg, - tokens_used=tokens_used - ) diff --git a/apps/agents/src/swarm/repl/__init__.py b/apps/agents/src/swarm/repl/__init__.py deleted file mode 100644 index 2a1cb40c..00000000 --- a/apps/agents/src/swarm/repl/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .repl import run_demo_loop diff --git a/apps/agents/src/swarm/repl/repl.py b/apps/agents/src/swarm/repl/repl.py deleted file mode 100644 index 50c3d0ee..00000000 --- a/apps/agents/src/swarm/repl/repl.py +++ /dev/null @@ -1,87 +0,0 @@ -import json - -from swarm import Swarm - - -def process_and_print_streaming_response(response): - content = "" - last_sender = "" - - for chunk in response: - if "sender" in chunk: - last_sender = chunk["sender"] - - if "content" in chunk and chunk["content"] is not None: - if not content and last_sender: - print(f"\033[94m{last_sender}:\033[0m", end=" ", flush=True) - last_sender = "" - print(chunk["content"], end="", flush=True) - content += chunk["content"] - - if "tool_calls" in chunk and chunk["tool_calls"] is not None: - for tool_call in chunk["tool_calls"]: - f = tool_call["function"] - name = f["name"] - if not name: - continue - print(f"\033[94m{last_sender}: \033[95m{name}\033[0m()") - - if "delim" in chunk and chunk["delim"] == "end" and content: - print() # End of response message - content = "" - - if "response" in chunk: - return chunk["response"] - - -def pretty_print_messages(messages) -> None: - for message in messages: - if message["role"] != "assistant": - continue - - # print agent name in blue - print(f"\033[94m{message['sender']}\033[0m:", end=" ") - - # print response, if any - if message["content"]: - print(message["content"]) - - # print tool calls in purple, if any - tool_calls = message.get("tool_calls") or [] - if len(tool_calls) > 1: - print() - for tool_call in tool_calls: - f = tool_call["function"] - name, args = f["name"], f["arguments"] - arg_str = json.dumps(json.loads(args)).replace(":", "=") - print(f"\033[95m{name}\033[0m({arg_str[1:-1]})") - - -def run_demo_loop( - starting_agent, context_variables=None, stream=False, debug=False -) -> None: - client = Swarm() - print("Starting Swarm CLI 🐝") - - messages = [] - agent = starting_agent - - while True: - user_input = input("\033[90mUser\033[0m: ") - messages.append({"role": "user", "content": user_input}) - - response = client.run( - agent=agent, - messages=messages, - context_variables=context_variables or {}, - stream=stream, - debug=debug, - ) - - if stream: - response = process_and_print_streaming_response(response) - else: - pretty_print_messages(response.messages) - - messages.extend(response.messages) - agent = response.agent diff --git a/apps/agents/src/swarm/types.py b/apps/agents/src/swarm/types.py deleted file mode 100644 index 1c6b87de..00000000 --- a/apps/agents/src/swarm/types.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -from openai.types.chat import ChatCompletionMessage -from openai.types.chat.chat_completion_message_tool_call import ( - ChatCompletionMessageToolCall, - Function, -) -from typing import List, Callable, Union, Optional, Dict - -# Third-party imports -from pydantic import BaseModel - -AgentFunction = Callable[[], Union[str, "Agent", dict]] - -class Agent(BaseModel): - name: str = "Agent" - model: str = "gpt-4o" - type: str = "" - instructions: Union[str, Callable[[], str]] = "You are a helpful agent.", - description: str = "This is a helpful agent." - candidate_parent_functions: Dict[str, AgentFunction] = {} - parent_function: AgentFunction = None - child_functions: Dict[str, AgentFunction] = {} - internal_tools: List[Dict] = [] - external_tools: List[Dict] = [] - tool_choice: str = None - parallel_tool_calls: bool = True - respond_to_user: bool = True - history: List[Dict] = [] - children_names: List[str] = [] - children: Dict[str, "Agent"] = {} - most_recent_parent: Optional["Agent"] = None - parent: "Agent" = None - -class Response(BaseModel): - messages: List = [] - agent: Optional[Agent] = None - context_variables: dict = {} - error_msg: Optional[str] = "" - tokens_used: dict = {} - -class Result(BaseModel): - """ - Encapsulates the possible return values for an agent function. - - Attributes: - value (str): The result value as a string. - agent (Agent): The agent instance, if applicable. - context_variables (dict): A dictionary of context variables. - """ - - value: str = "" - agent: Optional[Agent] = None - context_variables: dict = {} \ No newline at end of file diff --git a/apps/agents/src/swarm/util.py b/apps/agents/src/swarm/util.py deleted file mode 100644 index d2a3fc2f..00000000 --- a/apps/agents/src/swarm/util.py +++ /dev/null @@ -1,175 +0,0 @@ -import inspect -import json -from datetime import datetime -import os -from dotenv import load_dotenv -from src.utils.common import read_json_from_file, get_api_key - -load_dotenv() -OPENAI_API_KEY = get_api_key("OPENAI_API_KEY") - -def debug_print(debug: bool, *args: str) -> None: - if not debug: - return - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - message = " ".join(map(str, args)) - print(f"\033[97m[\033[90m{timestamp}\033[97m]\033[90m {message}\033[0m") - -def merge_fields(target, source): - for key, value in source.items(): - if isinstance(value, str): - target[key] += value - elif value is not None and isinstance(value, dict): - merge_fields(target[key], value) - - -def merge_chunk(final_response: dict, delta: dict) -> None: - delta.pop("role", None) - merge_fields(final_response, delta) - - tool_calls = delta.get("tool_calls") - if tool_calls and len(tool_calls) > 0: - index = tool_calls[0].pop("index") - merge_fields(final_response["tool_calls"][index], tool_calls[0]) - - -def function_to_json(func) -> dict: - """ - Converts a Python function into a JSON-serializable dictionary - that describes the function's signature, including its name, - description, and parameters. - - Args: - func: The function to be converted. - - Returns: - A dictionary representing the function's signature in JSON format. - """ - type_map = { - str: "string", - int: "integer", - float: "number", - bool: "boolean", - list: "array", - dict: "object", - type(None): "null", - } - - try: - signature = inspect.signature(func) - except ValueError as e: - raise ValueError( - f"Failed to get signature for function {func.__name__}: {str(e)}" - ) - - parameters = {} - for param in signature.parameters.values(): - try: - param_type = type_map.get(param.annotation, "string") - except KeyError as e: - raise KeyError( - f"Unknown type annotation {param.annotation} for parameter {param.name}: {str(e)}" - ) - parameters[param.name] = {"type": param_type} - - required = [ - param.name - for param in signature.parameters.values() - if param.default == inspect._empty - ] - - return { - "type": "function", - "function": { - "name": func.__name__, - "description": func.__doc__ or "", - "parameters": { - "type": "object", - "properties": parameters, - "required": required, - }, - }, - } - -def get_current_turn_messages(messages, only_user = False): - if only_user: - return [msg for msg in messages if msg.get("current_turn") and msg.get("role") == "user"] - else: - return [msg for msg in messages if msg.get("current_turn")] - -def arrange_messages_keys_in_order(messages): - """Arranges message keys in a specific order: id, role, sender, relevant_agents, content, created_at, timestamp, followed by rest alphabetically""" - key_order = ['role', 'sender', 'content', 'created_at'] - - def sort_keys(message): - # Create new dict with specified key order - ordered = {} - # Add keys in specified order if they exist - for key in key_order: - if key in message: - ordered[key] = message[key] - # Add remaining keys in alphabetical order - for key in sorted(message.keys()): - if key not in key_order: - ordered[key] = message[key] - return ordered - - return [sort_keys(message) for message in messages] - -def remove_irrelevant_messages(messages): - """Removes all messages from and including the latest user message""" - for i in range(len(messages)-1, -1, -1): - if messages[i].get("role") == "user": - return messages[:i] - return messages - -def update_histories(active_agent, message): - active_agent.history.append(message) - return active_agent - -def remove_none_fields(message): - return {k: v for k, v in message.items() if v is not None} - -def add_message_metadata(message, active_agent): - message = remove_none_fields(message) - message["created_at"] = datetime.now().isoformat() - message["current_turn"] = True - - if active_agent.respond_to_user: - message["response_type"] = "external" - else: - message["response_type"] = "internal" - - return message - -def check_and_remove_repeat_tool_call_to_child(agent, messages): - # If in the current turn, the most recent assistant message (need not be the last message overall, just needs to be the last message with role as assistant) is a tool call from a child agent, which transfers control to the agent using its parent function, then remove the tool call to transfer to that child again from this agent. This is to prevent back and forth between this agent and the child agent. - for message in reversed(messages): - if message.get("role") == "assistant" and message.get("sender") in agent.children_names and message.get("tool_calls"): - tool_call = message.get("tool_calls")[0] - child_agent = agent.children.get(message.get("sender"), None) - if not child_agent: - continue - child_agent_name = child_agent.name - if tool_call.get("function").get("name") == child_agent.parent_function: - agent.children_names.remove(child_agent_name) - agent.children.pop(child_agent_name) - agent.child_functions.pop(child_agent_name) - break - return agent - -def update_tokens_used(provider, model, tokens_used, completion): - provider_model = f"{provider}/{model}" - input_tokens = completion.usage.prompt_tokens - output_tokens = completion.usage.completion_tokens - - if provider_model not in tokens_used: - tokens_used[provider_model] = { - 'input_tokens': 0, - 'output_tokens': 0, - } - - tokens_used[provider_model]['input_tokens'] += input_tokens - tokens_used[provider_model]['output_tokens'] += output_tokens - - return tokens_used \ No newline at end of file diff --git a/apps/agents/src/utils/common.py b/apps/agents/src/utils/common.py index 83a0cf7f..57c1c58c 100644 --- a/apps/agents/src/utils/common.py +++ b/apps/agents/src/utils/common.py @@ -4,7 +4,6 @@ import os import subprocess import sys import time -from collections import defaultdict from dotenv import load_dotenv from openai import OpenAI diff --git a/apps/agents/tests/interactive.py b/apps/agents/tests/interactive.py index 1cf6dcdc..1e38181a 100644 --- a/apps/agents/tests/interactive.py +++ b/apps/agents/tests/interactive.py @@ -87,18 +87,9 @@ if __name__ == "__main__": start_agent_name=start_agent_name, agent_configs=agent_configs, tool_configs=tool_configs, - return_diff_messages=config.get("return_diff_messages", True), - prompt_configs=prompt_configs, start_turn_with_start_agent=config.get("start_turn_with_start_agent", False), - children_aware_of_parent=config.get("children_aware_of_parent", False), - parent_has_child_history=config.get("parent_has_child_history", True), state=state, - additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL], - error_tool_call=config.get("error_tool_call", True), - max_messages_per_turn=config.get("max_messages_per_turn", 10), - max_messages_per_error_escalation_turn=config.get("max_messages_per_error_escalation_turn", 4), - escalate_errors=config.get("escalate_errors", True), - max_overall_turns=config.get("max_overall_turns", 10) + additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL] ) state = resp_state resp_messages = order_messages(resp_messages)