diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 651810028e2a..7ee414958c6c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,12 +1,31 @@ -- [ ] This change is worth documenting at https://docs.all-hands.dev/ -- [ ] Include this change in the Release Notes. If checked, you **must** provide an **end-user friendly** description for your change below +## Summary of PR -**End-user friendly description of the problem this fixes or functionality this introduces.** + +## Change Type ---- -**Summarize what the PR does, explaining any non-trivial design decisions.** + +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Refactor +- [ ] Other (dependency update, docs, typo fixes, etc.) ---- -**Link of any specific issues this addresses:** +## Checklist + +- [ ] I have read and reviewed the code and I understand what the code is doing. +- [ ] I have tested the code to the best of my ability and ensured it works as expected. + +## Fixes + + + +Resolves #(issue) + +## Release Notes + + + +- [ ] Include this change in the Release Notes. diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index 6c10bd4db193..ae9ca2bcff01 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -126,7 +126,7 @@ jobs: - name: Install Python dependencies using Poetry run: make install-python-dependencies POETRY_GROUP=main INSTALL_PLAYWRIGHT=0 - name: Create source distribution and Dockerfile - run: poetry run python3 openhands/runtime/utils/runtime_build.py --base_image ${{ matrix.base_image.image }} --build_folder containers/runtime --force_rebuild + run: poetry run python3 -m openhands.runtime.utils.runtime_build --base_image ${{ matrix.base_image.image }} --build_folder containers/runtime --force_rebuild - name: Lowercase Repository Owner run: | echo REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV diff --git a/README.md b/README.md index 3e19c70ff4f3..a336a386353f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
- Logo + Logo

OpenHands: Code Less, Make More

@@ -38,6 +38,12 @@ call APIs, and yes—even copy code snippets from StackOverflow. Learn more at [docs.all-hands.dev](https://docs.all-hands.dev), or [sign up for OpenHands Cloud](https://app.all-hands.dev) to get started. + +> [!IMPORTANT] +> **Upcoming change**: We are renaming our GitHub Org from `All-Hands-AI` to `OpenHands` on October 20th, 2025. +> Check the [tracking issue](https://github.com/All-Hands-AI/OpenHands/issues/11376) for more information. + + > [!IMPORTANT] > Using OpenHands for work? We'd love to chat! Fill out > [this short form](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform) diff --git a/containers/runtime/README.md b/containers/runtime/README.md index 9fbed76aa371..7671c3286588 100644 --- a/containers/runtime/README.md +++ b/containers/runtime/README.md @@ -6,7 +6,7 @@ that depends on the `base_image` **AND** a [Python source distribution](https:// The following command will generate a `Dockerfile` file for `nikolaik/python-nodejs:python3.12-nodejs22` (the default base image), an updated `config.sh` and the runtime source distribution files/folders into `containers/runtime`: ```bash -poetry run python3 openhands/runtime/utils/runtime_build.py \ +poetry run python3 -m openhands.runtime.utils.runtime_build \ --base_image nikolaik/python-nodejs:python3.12-nodejs22 \ --build_folder containers/runtime ``` diff --git a/enterprise/integrations/jira/jira_view.py b/enterprise/integrations/jira/jira_view.py index 1cc1e7104673..eeff968ec305 100644 --- a/enterprise/integrations/jira/jira_view.py +++ b/enterprise/integrations/jira/jira_view.py @@ -132,8 +132,10 @@ async def create_or_update_conversation(self, jinja_env: Environment) -> str: conversation_store = await ConversationStoreImpl.get_instance( config, user_id ) - metadata = await conversation_store.get_metadata(self.conversation_id) - if not metadata: + + try: + await conversation_store.get_metadata(self.conversation_id) + except FileNotFoundError: raise StartingConvoException('Conversation no longer exists.') provider_tokens = await self.saas_user_auth.get_provider_tokens() diff --git a/enterprise/integrations/jira_dc/jira_dc_view.py b/enterprise/integrations/jira_dc/jira_dc_view.py index 907d83bcd4ef..c60cbfc9829b 100644 --- a/enterprise/integrations/jira_dc/jira_dc_view.py +++ b/enterprise/integrations/jira_dc/jira_dc_view.py @@ -135,8 +135,10 @@ async def create_or_update_conversation(self, jinja_env: Environment) -> str: conversation_store = await ConversationStoreImpl.get_instance( config, user_id ) - metadata = await conversation_store.get_metadata(self.conversation_id) - if not metadata: + + try: + await conversation_store.get_metadata(self.conversation_id) + except FileNotFoundError: raise StartingConvoException('Conversation no longer exists.') provider_tokens = await self.saas_user_auth.get_provider_tokens() diff --git a/enterprise/integrations/linear/linear_view.py b/enterprise/integrations/linear/linear_view.py index c2c0292f53cb..a0cf69a5f857 100644 --- a/enterprise/integrations/linear/linear_view.py +++ b/enterprise/integrations/linear/linear_view.py @@ -132,8 +132,10 @@ async def create_or_update_conversation(self, jinja_env: Environment) -> str: conversation_store = await ConversationStoreImpl.get_instance( config, user_id ) - metadata = await conversation_store.get_metadata(self.conversation_id) - if not metadata: + + try: + await conversation_store.get_metadata(self.conversation_id) + except FileNotFoundError: raise StartingConvoException('Conversation no longer exists.') provider_tokens = await self.saas_user_auth.get_provider_tokens() diff --git a/enterprise/integrations/slack/slack_view.py b/enterprise/integrations/slack/slack_view.py index 4c5bc00cedc0..fdaed07971ba 100644 --- a/enterprise/integrations/slack/slack_view.py +++ b/enterprise/integrations/slack/slack_view.py @@ -263,8 +263,10 @@ async def create_or_update_conversation(self, jinja: Environment) -> str: # Check if conversation has been deleted # Update logic when soft delete is implemented conversation_store = await ConversationStoreImpl.get_instance(config, user_id) - metadata = await conversation_store.get_metadata(self.conversation_id) - if not metadata: + + try: + await conversation_store.get_metadata(self.conversation_id) + except FileNotFoundError: raise StartingConvoException('Conversation no longer exists.') provider_tokens = await saas_user_auth.get_provider_tokens() diff --git a/enterprise/migrations/versions/076_add_v1_tables.py b/enterprise/migrations/versions/076_add_v1_tables.py new file mode 100644 index 000000000000..1f8019766559 --- /dev/null +++ b/enterprise/migrations/versions/076_add_v1_tables.py @@ -0,0 +1,259 @@ +"""Sync DB with Models + +Revision ID: 076 +Revises: 075 +Create Date: 2025-10-05 11:28:41.772294 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +from openhands.app_server.app_conversation.app_conversation_models import ( + AppConversationStartTaskStatus, +) +from openhands.app_server.event_callback.event_callback_result_models import ( + EventCallbackResultStatus, +) + +# revision identifiers, used by Alembic. +revision: str = '076' +down_revision: Union[str, Sequence[str], None] = '075' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + op.add_column( + 'conversation_metadata', + sa.Column('max_budget_per_task', sa.Float(), nullable=True), + ) + op.add_column( + 'conversation_metadata', + sa.Column('cache_read_tokens', sa.Integer(), server_default='0'), + ) + op.add_column( + 'conversation_metadata', + sa.Column('cache_write_tokens', sa.Integer(), server_default='0'), + ) + op.add_column( + 'conversation_metadata', + sa.Column('reasoning_tokens', sa.Integer(), server_default='0'), + ) + op.add_column( + 'conversation_metadata', + sa.Column('context_window', sa.Integer(), server_default='0'), + ) + op.add_column( + 'conversation_metadata', + sa.Column('per_turn_token', sa.Integer(), server_default='0'), + ) + op.add_column( + 'conversation_metadata', + sa.Column( + 'conversation_version', sa.String(), nullable=False, server_default='V0' + ), + ) + op.create_index( + op.f('ix_conversation_metadata_conversation_version'), + 'conversation_metadata', + ['conversation_version'], + unique=False, + ) + op.add_column('conversation_metadata', sa.Column('sandbox_id', sa.String())) + op.create_index( + op.f('ix_conversation_metadata_sandbox_id'), + 'conversation_metadata', + ['sandbox_id'], + unique=False, + ) + op.create_table( + 'app_conversation_start_task', + sa.Column('id', sa.UUID(), nullable=False), + sa.Column('created_by_user_id', sa.String(), nullable=True), + sa.Column('status', sa.Enum(AppConversationStartTaskStatus), nullable=True), + sa.Column('detail', sa.String(), nullable=True), + sa.Column('app_conversation_id', sa.UUID(), nullable=True), + sa.Column('sandbox_id', sa.String(), nullable=True), + sa.Column('agent_server_url', sa.String(), nullable=True), + sa.Column('request', sa.JSON(), nullable=True), + sa.Column( + 'created_at', + sa.DateTime(timezone=True), + server_default=sa.text('(CURRENT_TIMESTAMP)'), + nullable=True, + ), + sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index( + op.f('ix_app_conversation_start_task_created_at'), + 'app_conversation_start_task', + ['created_at'], + unique=False, + ) + op.create_index( + op.f('ix_app_conversation_start_task_created_by_user_id'), + 'app_conversation_start_task', + ['created_by_user_id'], + unique=False, + ) + op.create_index( + op.f('ix_app_conversation_start_task_updated_at'), + 'app_conversation_start_task', + ['updated_at'], + unique=False, + ) + op.create_table( + 'event_callback', + sa.Column('id', sa.UUID(), nullable=False), + sa.Column('conversation_id', sa.UUID(), nullable=True), + sa.Column('processor', sa.JSON(), nullable=True), + sa.Column('event_kind', sa.String(), nullable=True), + sa.Column( + 'created_at', + sa.DateTime(timezone=True), + server_default=sa.text('(CURRENT_TIMESTAMP)'), + nullable=True, + ), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index( + op.f('ix_event_callback_created_at'), + 'event_callback', + ['created_at'], + unique=False, + ) + op.create_table( + 'event_callback_result', + sa.Column('id', sa.UUID(), nullable=False), + sa.Column('status', sa.Enum(EventCallbackResultStatus), nullable=True), + sa.Column('event_callback_id', sa.UUID(), nullable=True), + sa.Column('event_id', sa.UUID(), nullable=True), + sa.Column('conversation_id', sa.UUID(), nullable=True), + sa.Column('detail', sa.String(), nullable=True), + sa.Column( + 'created_at', + sa.DateTime(timezone=True), + server_default=sa.text('(CURRENT_TIMESTAMP)'), + nullable=True, + ), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index( + op.f('ix_event_callback_result_conversation_id'), + 'event_callback_result', + ['conversation_id'], + unique=False, + ) + op.create_index( + op.f('ix_event_callback_result_created_at'), + 'event_callback_result', + ['created_at'], + unique=False, + ) + op.create_index( + op.f('ix_event_callback_result_event_callback_id'), + 'event_callback_result', + ['event_callback_id'], + unique=False, + ) + op.create_index( + op.f('ix_event_callback_result_event_id'), + 'event_callback_result', + ['event_id'], + unique=False, + ) + op.create_table( + 'v1_remote_sandbox', + sa.Column('id', sa.String(), nullable=False), + sa.Column('created_by_user_id', sa.String(), nullable=True), + sa.Column('sandbox_spec_id', sa.String(), nullable=True), + sa.Column( + 'created_at', + sa.DateTime(timezone=True), + server_default=sa.text('(CURRENT_TIMESTAMP)'), + nullable=True, + ), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index( + op.f('ix_v1_remote_sandbox_created_at'), + 'v1_remote_sandbox', + ['created_at'], + unique=False, + ) + op.create_index( + op.f('ix_v1_remote_sandbox_created_by_user_id'), + 'v1_remote_sandbox', + ['created_by_user_id'], + unique=False, + ) + op.create_index( + op.f('ix_v1_remote_sandbox_sandbox_spec_id'), + 'v1_remote_sandbox', + ['sandbox_spec_id'], + unique=False, + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index( + op.f('ix_v1_remote_sandbox_sandbox_spec_id'), table_name='v1_remote_sandbox' + ) + op.drop_index( + op.f('ix_v1_remote_sandbox_created_by_user_id'), table_name='v1_remote_sandbox' + ) + op.drop_index( + op.f('ix_v1_remote_sandbox_created_at'), table_name='v1_remote_sandbox' + ) + op.drop_table('v1_remote_sandbox') + op.drop_index( + op.f('ix_event_callback_result_event_id'), + table_name='event_callback_result', + ) + op.drop_index( + op.f('ix_event_callback_result_event_callback_id'), + table_name='event_callback_result', + ) + op.drop_index( + op.f('ix_event_callback_result_created_at'), + table_name='event_callback_result', + ) + op.drop_index( + op.f('ix_event_callback_result_conversation_id'), + table_name='event_callback_result', + ) + op.drop_table('event_callback_result') + op.drop_index(op.f('ix_event_callback_created_at'), table_name='event_callback') + op.drop_table('event_callback') + op.drop_index( + op.f('ix_app_conversation_start_task_updated_at'), + table_name='app_conversation_start_task', + ) + op.drop_index( + op.f('ix_app_conversation_start_task_created_by_user_id'), + table_name='app_conversation_start_task', + ) + op.drop_index( + op.f('ix_app_conversation_start_task_created_at'), + table_name='app_conversation_start_task', + ) + op.drop_table('app_conversation_start_task') + op.drop_column('conversation_metadata', 'sandbox_id') + op.drop_column('conversation_metadata', 'conversation_version') + op.drop_column('conversation_metadata', 'per_turn_token') + op.drop_column('conversation_metadata', 'context_window') + op.drop_column('conversation_metadata', 'reasoning_tokens') + op.drop_column('conversation_metadata', 'cache_write_tokens') + op.drop_column('conversation_metadata', 'cache_read_tokens') + op.drop_column('conversation_metadata', 'max_budget_per_task') + op.execute('DROP TYPE appconversationstarttaskstatus') + op.execute('DROP TYPE eventcallbackresultstatus') + # ### end Alembic commands ### diff --git a/enterprise/poetry.lock b/enterprise/poetry.lock index 87d423e642c5..d2c45e0c0f6c 100644 --- a/enterprise/poetry.lock +++ b/enterprise/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiofiles" @@ -148,6 +148,25 @@ files = [ frozenlist = ">=1.1.0" typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} +[[package]] +name = "aiosqlite" +version = "0.21.0" +description = "asyncio bridge to the standard sqlite3 module" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0"}, + {file = "aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3"}, +] + +[package.dependencies] +typing_extensions = ">=4.0" + +[package.extras] +dev = ["attribution (==1.7.1)", "black (==24.3.0)", "build (>=1.2)", "coverage[toml] (==7.6.10)", "flake8 (==7.0.0)", "flake8-bugbear (==24.12.12)", "flit (==3.10.1)", "mypy (==1.14.1)", "ufmt (==2.5.1)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==8.1.3)", "sphinx-mdinclude (==0.6.1)"] + [[package]] name = "alembic" version = "1.16.5" @@ -1061,7 +1080,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\" or os_name == \"nt\"", dev = "os_name == \"nt\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""} +markers = {main = "platform_system == \"Windows\" or os_name == \"nt\" or sys_platform == \"win32\"", dev = "os_name == \"nt\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "comm" @@ -1797,6 +1816,25 @@ files = [ {file = "durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba"}, ] +[[package]] +name = "ecdsa" +version = "0.19.1" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6" +groups = ["main"] +files = [ + {file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"}, + {file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + [[package]] name = "email-validator" version = "2.3.0" @@ -1968,38 +2006,79 @@ websockets = ["websockets (>=15.0.1)"] [[package]] name = "fastuuid" -version = "0.12.0" +version = "0.13.5" description = "Python bindings to Rust's UUID library." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "fastuuid-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:22a900ef0956aacf862b460e20541fdae2d7c340594fe1bd6fdcb10d5f0791a9"}, - {file = "fastuuid-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0302f5acf54dc75de30103025c5a95db06d6c2be36829043a0aa16fc170076bc"}, - {file = "fastuuid-0.12.0-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:7946b4a310cfc2d597dcba658019d72a2851612a2cebb949d809c0e2474cf0a6"}, - {file = "fastuuid-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:a1b6764dd42bf0c46c858fb5ade7b7a3d93b7a27485a7a5c184909026694cd88"}, - {file = "fastuuid-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bced35269315d16fe0c41003f8c9d63f2ee16a59295d90922cad5e6a67d0418"}, - {file = "fastuuid-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82106e4b0a24f4f2f73c88f89dadbc1533bb808900740ca5db9bbb17d3b0c824"}, - {file = "fastuuid-0.12.0-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:4db1bc7b8caa1d7412e1bea29b016d23a8d219131cff825b933eb3428f044dca"}, - {file = "fastuuid-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:07afc8e674e67ac3d35a608c68f6809da5fab470fb4ef4469094fdb32ba36c51"}, - {file = "fastuuid-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:328694a573fe9dce556b0b70c9d03776786801e028d82f0b6d9db1cb0521b4d1"}, - {file = "fastuuid-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02acaea2c955bb2035a7d8e7b3fba8bd623b03746ae278e5fa932ef54c702f9f"}, - {file = "fastuuid-0.12.0-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:ed9f449cba8cf16cced252521aee06e633d50ec48c807683f21cc1d89e193eb0"}, - {file = "fastuuid-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:0df2ea4c9db96fd8f4fa38d0e88e309b3e56f8fd03675a2f6958a5b082a0c1e4"}, - {file = "fastuuid-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7fe2407316a04ee8f06d3dbc7eae396d0a86591d92bafe2ca32fce23b1145786"}, - {file = "fastuuid-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b31dd488d0778c36f8279b306dc92a42f16904cba54acca71e107d65b60b0c"}, - {file = "fastuuid-0.12.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:b19361ee649365eefc717ec08005972d3d1eb9ee39908022d98e3bfa9da59e37"}, - {file = "fastuuid-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8fc66b11423e6f3e1937385f655bedd67aebe56a3dcec0cb835351cfe7d358c9"}, - {file = "fastuuid-0.12.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2925f67b88d47cb16aa3eb1ab20fdcf21b94d74490e0818c91ea41434b987493"}, - {file = "fastuuid-0.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7b15c54d300279ab20a9cc0579ada9c9f80d1bc92997fc61fb7bf3103d7cb26b"}, - {file = "fastuuid-0.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:458f1bc3ebbd76fdb89ad83e6b81ccd3b2a99fa6707cd3650b27606745cfb170"}, - {file = "fastuuid-0.12.0-cp38-cp38-manylinux_2_34_x86_64.whl", hash = "sha256:a8f0f83fbba6dc44271a11b22e15838641b8c45612cdf541b4822a5930f6893c"}, - {file = "fastuuid-0.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:7cfd2092253d3441f6a8c66feff3c3c009da25a5b3da82bc73737558543632be"}, - {file = "fastuuid-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9303617e887429c193d036d47d0b32b774ed3618431123e9106f610d601eb57e"}, - {file = "fastuuid-0.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8790221325b376e1122e95f865753ebf456a9fb8faf0dca4f9bf7a3ff620e413"}, - {file = "fastuuid-0.12.0-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:e4b12d3e23515e29773fa61644daa660ceb7725e05397a986c2109f512579a48"}, - {file = "fastuuid-0.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:e41656457c34b5dcb784729537ea64c7d9bbaf7047b480c6c6a64c53379f455a"}, - {file = "fastuuid-0.12.0.tar.gz", hash = "sha256:d0bd4e5b35aad2826403f4411937c89e7c88857b1513fe10f696544c03e9bd8e"}, + {file = "fastuuid-0.13.5-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b9edf8ee30718aee787cdd2e9e1ff3d4a3ec6ddb32fba0a23fa04956df69ab07"}, + {file = "fastuuid-0.13.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f67ea1e25c5e782f7fb5aaa5208f157d950401dd9321ce56bcc6d4dc3d72ed60"}, + {file = "fastuuid-0.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ff3fc87e1f19603dd53c38f42c2ea8d5d5462554deab69e9cf1800574e4756c"}, + {file = "fastuuid-0.13.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6e5337fa7698dc52bc724da7e9239e93c5b24a09f6904b8660dfb8c41ce3dee"}, + {file = "fastuuid-0.13.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9db596023c10dabb12489a88c51b75297c3a2478cb2be645e06905934e7b9fc"}, + {file = "fastuuid-0.13.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:191ff6192fe53c5fc9d4d241ee1156b30a7ed6f1677b1cc2423e7ecdbc26222b"}, + {file = "fastuuid-0.13.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:348ce9f296dda701ba46d8dceeff309f90dbc75dd85080bbed2b299aa908890a"}, + {file = "fastuuid-0.13.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:46954fb644995d7fc8bbd710fbd4c65cedaa48c921c86fdbafef0229168a8c96"}, + {file = "fastuuid-0.13.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22da0f66041e1c10c7d465b495cc6cd8e17e080dda34b4bd5ff5240b860fbb82"}, + {file = "fastuuid-0.13.5-cp310-cp310-win32.whl", hash = "sha256:3e6b548f06c1ed7bad951a17a09eef69d6f24eb2b874cb4833e26b886d82990f"}, + {file = "fastuuid-0.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:c82838e52189d16b1307631179cb2cd37778dd8f4ddc00e9ce3c26f920b3b2f7"}, + {file = "fastuuid-0.13.5-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c122558ca4b5487e2bd0863467e4ccfe636afd1274803741487d48f2e32ea0e1"}, + {file = "fastuuid-0.13.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d7abd42a03a17a681abddd19aa4d44ca2747138cf8a48373b395cf1341a10de2"}, + {file = "fastuuid-0.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2705cf7c2d6f7c03053404b75a4c44f872a73f6f9d5ea34f1dc6bba400c4a97c"}, + {file = "fastuuid-0.13.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d220a056fcbad25932c1f25304261198612f271f4d150b2a84e81adb877daf7"}, + {file = "fastuuid-0.13.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f29f93b5a0c5f5579f97f77d5319e9bfefd61d8678ec59d850201544faf33bf"}, + {file = "fastuuid-0.13.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:399d86623fb806151b1feb9fdd818ebfc1d50387199a35f7264f98dfc1540af5"}, + {file = "fastuuid-0.13.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:689e8795a1edd573b2c9a455024e4edf605a9690339bba29709857f7180894ea"}, + {file = "fastuuid-0.13.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:25e82c4a1734da168b36f7308e397afbe9c9b353799a9c69563a605f11dd4641"}, + {file = "fastuuid-0.13.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f62299e3cca69aad6a6fb37e26e45055587954d498ad98903fea24382377ea0e"}, + {file = "fastuuid-0.13.5-cp311-cp311-win32.whl", hash = "sha256:68227f2230381b89fb1ad362ca6e433de85c6c11c36312b41757cad47b8a8e32"}, + {file = "fastuuid-0.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:4a32306982bd031cb20d5d1a726b7b958a55babebd2300ce6c8e352d3496e931"}, + {file = "fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35fe8045e866bc6846f8de6fa05acb1de0c32478048484a995e96d31e21dff2a"}, + {file = "fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:02a460333f52d731a006d18a52ef6fcb2d295a1f5b1a5938d30744191b2f77b7"}, + {file = "fastuuid-0.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:74b0e4f8c307b9f477a5d7284db4431ce53a3c1e3f4173db7a97db18564a6202"}, + {file = "fastuuid-0.13.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6955a99ef455c2986f3851f4e0ccc35dec56ac1a7720f2b92e88a75d6684512e"}, + {file = "fastuuid-0.13.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10c77b826738c1a27dcdaa92ea4dc1ec9d869748a99e1fde54f1379553d4854"}, + {file = "fastuuid-0.13.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb25dccbeb249d16d5e664f65f17ebec05136821d5ef462c4110e3f76b86fb86"}, + {file = "fastuuid-0.13.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5becc646a3eeafb76ce0a6783ba190cd182e3790a8b2c78ca9db2b5e87af952"}, + {file = "fastuuid-0.13.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:69b34363752d06e9bb0dbdf02ae391ec56ac948c6f2eb00be90dad68e80774b9"}, + {file = "fastuuid-0.13.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57d0768afcad0eab8770c9b8cf904716bd3c547e8b9a4e755ee8a673b060a3a3"}, + {file = "fastuuid-0.13.5-cp312-cp312-win32.whl", hash = "sha256:8ac6c6f5129d52eaa6ef9ea4b6e2f7c69468a053f3ab8e439661186b9c06bb85"}, + {file = "fastuuid-0.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:ad630e97715beefef07ec37c9c162336e500400774e2c1cbe1a0df6f80d15b9a"}, + {file = "fastuuid-0.13.5-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ea17dfd35e0e91920a35d91e65e5f9c9d1985db55ac4ff2f1667a0f61189cefa"}, + {file = "fastuuid-0.13.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:be6ad91e5fefbcc2a4b478858a2715e386d405834ea3ae337c3b6b95cc0e47d6"}, + {file = "fastuuid-0.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ea6df13a306aab3e0439d58c312ff1e6f4f07f09f667579679239b4a6121f64a"}, + {file = "fastuuid-0.13.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2354c1996d3cf12dc2ba3752e2c4d6edc46e1a38c63893146777b1939f3062d4"}, + {file = "fastuuid-0.13.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6cf9b7469fc26d1f9b1c43ac4b192e219e85b88fdf81d71aa755a6c08c8a817"}, + {file = "fastuuid-0.13.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92ba539170097b9047551375f1ca09d8d2b4aefcc79eeae3e1c43fe49b42072e"}, + {file = "fastuuid-0.13.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:dbb81d05617bc2970765c1ad82db7e8716f6a2b7a361a14b83de5b9240ade448"}, + {file = "fastuuid-0.13.5-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:d973bd6bf9d754d3cca874714ac0a6b22a47f239fb3d3c8687569db05aac3471"}, + {file = "fastuuid-0.13.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e725ceef79486423f05ee657634d4b4c1ca5fb2c8a94e0708f5d6356a83f2a83"}, + {file = "fastuuid-0.13.5-cp313-cp313-win32.whl", hash = "sha256:a1c430a332ead0b2674f1ef71b17f43b8139ec5a4201182766a21f131a31e021"}, + {file = "fastuuid-0.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:241fdd362fd96e6b337db62a65dd7cb3dfac20adf854573247a47510e192db6f"}, + {file = "fastuuid-0.13.5-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e353c0a0d978a5ecd97171ac4fb7f55a6bd6cbae90f1ec4e828e5317f11b995e"}, + {file = "fastuuid-0.13.5-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:904ac3eb37f4742e23f6a51be0d0451d1d3aceb50df8dac7afc6bf5209793650"}, + {file = "fastuuid-0.13.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6b070e0dc1965d53b9e07c291537095ececf7d7e36e60aed9b22400fa6c5c7f"}, + {file = "fastuuid-0.13.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0225f8bc78071a191cb458b3b0e23b04a7f03013575b8a3083da2a84c450e200"}, + {file = "fastuuid-0.13.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3f3f8f10b962cf2e11d3affc0cf2697ac5c9accc0d282dce981ed555a44ce15"}, + {file = "fastuuid-0.13.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1ae87968614fe6d3029a8198671b5893341aac9459289e93d201027be9ea7e8"}, + {file = "fastuuid-0.13.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6ca01f5e614530a1a858bf185dd5556805a4c11b6eba0a2536890b68ed954922"}, + {file = "fastuuid-0.13.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d3b6b10d78b9f7056445ac377612443980349da7221a3dd3e3f382f7c437be3"}, + {file = "fastuuid-0.13.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ae95e4dcf94775d948ebb843f4443d33cd224bb31174030e106ee3cab66527c"}, + {file = "fastuuid-0.13.5-cp38-cp38-win32.whl", hash = "sha256:5d753bc9ba8de6dd9caa8bbac045578c2fbe1c6ae40c2026b614676776fbe9dc"}, + {file = "fastuuid-0.13.5-cp38-cp38-win_amd64.whl", hash = "sha256:f9530f1328b05b80c6fa111e7f2a5d55fa30fbbd72d708326d0c7b55b67ed772"}, + {file = "fastuuid-0.13.5-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ed78153c589e5efb34faaa216836a5bf8a0b9d34e82183203166011238d9ed13"}, + {file = "fastuuid-0.13.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a67e18c2d7fba8be6ea4aed8ca5a20fcf273f003efa01c1f33a096b72537e69e"}, + {file = "fastuuid-0.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e362a3d66874d3d11a1ee9a8e717e32c2817cdb5d7a4e913290bf6e0f2a7fd8"}, + {file = "fastuuid-0.13.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9baa9c33848ec0926231e7ecfef9e02faa0f6d24265b64108ea41f7a0bb3f48"}, + {file = "fastuuid-0.13.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec6dab282162c19ec2172f33bafd467cffe26b92345789278adcbec19428d1"}, + {file = "fastuuid-0.13.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2424a90688dbc44f119686fa452ff21aa106c9da258214f577816462ad606d5"}, + {file = "fastuuid-0.13.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f8f2cc6972941ab030f3776961ed8454772c3acad88781fc262d71514df89973"}, + {file = "fastuuid-0.13.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a41257ea172b5de199c3cfa71cc6c574dcf22367fe51e26cba0d359107f11f30"}, + {file = "fastuuid-0.13.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cade5f3b8023dbba5a006e5685a7baf0d7a30c43cea17113768aa9ef9582d799"}, + {file = "fastuuid-0.13.5-cp39-cp39-win32.whl", hash = "sha256:880f0d03ad2518b96757ca422cba6ff76cea5464db2b3ad75c32acf1890e058f"}, + {file = "fastuuid-0.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:ebe95b730f81808eabc90247ac3d412b96d9fae1c406760b163bb9f134b7af69"}, + {file = "fastuuid-0.13.5.tar.gz", hash = "sha256:d4976821ab424d41542e1ea39bc828a9d454c3f8a04067c06fca123c5b95a1a1"}, ] [[package]] @@ -4187,14 +4266,14 @@ dev = ["Sphinx (>=5.1.1)", "black (==24.8.0)", "build (>=0.10.0)", "coverage[tom [[package]] name = "libtmux" -version = "0.39.0" +version = "0.46.2" description = "Typed library that provides an ORM wrapper for tmux, a terminal multiplexer." optional = false python-versions = "<4.0,>=3.9" groups = ["main"] files = [ - {file = "libtmux-0.39.0-py3-none-any.whl", hash = "sha256:6b6e338be2727f67aa6b7eb67fa134368fa3c3eac5df27565396467692891c1e"}, - {file = "libtmux-0.39.0.tar.gz", hash = "sha256:59346aeef3c0d6017f3bc5e23248d43cdf50f32b775b9cb5d9ff5e2e5f3059f4"}, + {file = "libtmux-0.46.2-py3-none-any.whl", hash = "sha256:6c32dbf22bde8e5e33b2714a4295f6e838dc640f337cd4c085a044f6828c7793"}, + {file = "libtmux-0.46.2.tar.gz", hash = "sha256:9a398fec5d714129c8344555d466e1a903dfc0f741ba07aabe75a8ceb25c5dda"}, ] [[package]] @@ -4228,26 +4307,24 @@ valkey = ["valkey (>=6)"] [[package]] name = "litellm" -version = "1.76.1" +version = "1.77.7" description = "Library to easily interface with LLM API providers" optional = false -python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" +python-versions = ">=3.8.1,<4.0, !=3.9.7" groups = ["main"] -files = [ - {file = "litellm-1.76.1-py3-none-any.whl", hash = "sha256:938f05075372f26098211ea9b3cb0a6bb7b46111330226b70d42d40bd307812f"}, - {file = "litellm-1.76.1.tar.gz", hash = "sha256:d5a3a3efda04999b60ec0d1c29c1eaaa12f89a7b29db4bda691c7fb55b4fa6ad"}, -] +files = [] +develop = false [package.dependencies] aiohttp = ">=3.10" click = "*" -fastuuid = ">=0.12.0" +fastuuid = ">=0.13.0" httpx = ">=0.23.0" importlib-metadata = ">=6.8.0" -jinja2 = ">=3.1.2,<4.0.0" -jsonschema = ">=4.22.0,<5.0.0" +jinja2 = "^3.1.2" +jsonschema = "^4.22.0" openai = ">=1.99.5" -pydantic = ">=2.5.0,<3.0.0" +pydantic = "^2.5.0" python-dotenv = ">=0.2.0" tiktoken = ">=0.7.0" tokenizers = "*" @@ -4256,10 +4333,16 @@ tokenizers = "*" caching = ["diskcache (>=5.6.1,<6.0.0)"] extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-iam (>=2.19.1,<3.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "redisvl (>=0.4.1,<0.5.0) ; python_version >= \"3.9\" and python_version < \"3.14\"", "resend (>=0.8.0,<0.9.0)"] mlflow = ["mlflow (>3.1.4) ; python_version >= \"3.10\""] -proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "azure-identity (>=1.15.0,<2.0.0)", "azure-storage-blob (>=12.25.1,<13.0.0)", "backoff", "boto3 (==1.36.0)", "cryptography (>=43.0.1,<44.0.0)", "fastapi (>=0.115.5,<0.116.0)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "litellm-enterprise (==0.1.19)", "litellm-proxy-extras (==0.2.18)", "mcp (>=1.10.0,<2.0.0) ; python_version >= \"3.10\"", "orjson (>=3.9.7,<4.0.0)", "polars (>=1.31.0,<2.0.0) ; python_version >= \"3.10\"", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rich (==13.7.1)", "rq", "uvicorn (>=0.29.0,<0.30.0)", "uvloop (>=0.21.0,<0.22.0) ; sys_platform != \"win32\"", "websockets (>=13.1.0,<14.0.0)"] +proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "azure-identity (>=1.15.0,<2.0.0)", "azure-storage-blob (>=12.25.1,<13.0.0)", "backoff", "boto3 (==1.36.0)", "cryptography", "fastapi (>=0.115.5,<0.116.0)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "litellm-enterprise (==0.1.20)", "litellm-proxy-extras (==0.2.25)", "mcp (>=1.10.0,<2.0.0) ; python_version >= \"3.10\"", "orjson (>=3.9.7,<4.0.0)", "polars (>=1.31.0,<2.0.0) ; python_version >= \"3.10\"", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rich (==13.7.1)", "rq", "uvicorn (>=0.29.0,<0.30.0)", "uvloop (>=0.21.0,<0.22.0) ; sys_platform != \"win32\"", "websockets (>=13.1.0,<14.0.0)"] semantic-router = ["semantic-router ; python_version >= \"3.9\""] utils = ["numpydoc"] +[package.source] +type = "git" +url = "https://github.com/BerriAI/litellm.git" +reference = "v1.77.7.dev9" +resolved_reference = "763d2f8ccdd8412dbe6d4ac0e136d9ac34dcd4c0" + [[package]] name = "llvmlite" version = "0.44.0" @@ -5430,9 +5513,36 @@ youtube-transcript-api = ">=0.6.2" [package.extras] llama = ["llama-index (>=0.12.29,<0.13.0)", "llama-index-core (>=0.12.29,<0.13.0)", "llama-index-retrievers-bm25 (>=0.5.2,<0.6.0)"] +[[package]] +name = "openhands-agent-server" +version = "1.0.0" +description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent" +optional = false +python-versions = ">=3.12" +groups = ["main"] +files = [] +develop = false + +[package.dependencies] +aiosqlite = ">=0.19" +alembic = ">=1.13" +docker = ">=7.1,<8" +fastapi = ">=0.104" +pydantic = ">=2" +sqlalchemy = ">=2" +uvicorn = ">=0.31.1" +websockets = ">=12" + +[package.source] +type = "git" +url = "https://github.com/All-Hands-AI/agent-sdk.git" +reference = "08cf609a996523c0199c61c768d74417b7e96109" +resolved_reference = "08cf609a996523c0199c61c768d74417b7e96109" +subdirectory = "openhands/agent_server" + [[package]] name = "openhands-ai" -version = "0.57.0" +version = "0.59.0" description = "OpenHands: Code Less, Make More" optional = false python-versions = "^3.12,<3.14" @@ -5444,6 +5554,7 @@ develop = true aiohttp = ">=3.9.0,!=3.11.13" anthropic = {version = "*", extras = ["vertex"]} anyio = "4.9.0" +asyncpg = "^0.30.0" bashlex = "^0.18" boto3 = "*" browsergym-core = "0.13.3" @@ -5465,21 +5576,26 @@ joblib = "*" json-repair = "*" jupyter_kernel_gateway = "*" kubernetes = "^33.1.0" -libtmux = ">=0.37,<0.40" -litellm = ">=1.74.3, <1.77.2, !=1.64.4, !=1.67.*" +libtmux = ">=0.46.2" +litellm = ">=1.74.3, <1.78.0, !=1.64.4, !=1.67.*" memory-profiler = "^0.61.0" numpy = "*" openai = "1.99.9" openhands-aci = "0.3.2" +openhands-agent-server = {git = "https://github.com/All-Hands-AI/agent-sdk.git", rev = "08cf609a996523c0199c61c768d74417b7e96109", subdirectory = "openhands/agent_server"} +openhands-sdk = {git = "https://github.com/All-Hands-AI/agent-sdk.git", rev = "08cf609a996523c0199c61c768d74417b7e96109", subdirectory = "openhands/sdk"} opentelemetry-api = "^1.33.1" opentelemetry-exporter-otlp-proto-grpc = "^1.33.1" pathspec = "^0.12.1" pexpect = "*" +pg8000 = "^1.31.5" pillow = "^11.3.0" +playwright = "^1.55.0" poetry = "^2.1.2" prompt-toolkit = "^3.0.50" protobuf = "^5.0.0,<6.0.0" psutil = "*" +pybase62 = "^1.0.0" pygithub = "^2.5.0" pyjwt = "^2.9.0" pylatexenc = "*" @@ -5488,6 +5604,7 @@ PyPDF2 = "*" python-docx = "*" python-dotenv = "*" python-frontmatter = "^1.1.0" +python-jose = {version = ">=3.3", extras = ["cryptography"]} python-json-logger = "^3.2.1" python-multipart = "*" python-pptx = "*" @@ -5500,6 +5617,7 @@ redis = ">=5.2,<7.0" requests = "^2.32.5" setuptools = ">=78.1.1" shellingham = "^1.5.4" +sqlalchemy = {version = "^2.0.40", extras = ["asyncio"]} sse-starlette = "^3.0.2" starlette = "^0.48.0" tenacity = ">=8.5,<10.0" @@ -5519,6 +5637,35 @@ third-party-runtimes = ["daytona (==0.24.2)", "e2b-code-interpreter (>=2.0.0,<3. type = "directory" url = ".." +[[package]] +name = "openhands-sdk" +version = "1.0.0" +description = "OpenHands SDK - Core functionality for building AI agents" +optional = false +python-versions = ">=3.12" +groups = ["main"] +files = [] +develop = false + +[package.dependencies] +fastmcp = ">=2.11.3" +litellm = {git = "https://github.com/BerriAI/litellm.git", rev = "v1.77.7.dev9"} +pydantic = ">=2.11.7" +python-frontmatter = ">=1.1.0" +python-json-logger = ">=3.3.0" +tenacity = ">=9.1.2" +websockets = ">=12" + +[package.extras] +boto3 = ["boto3 (>=1.35.0)"] + +[package.source] +type = "git" +url = "https://github.com/All-Hands-AI/agent-sdk.git" +reference = "08cf609a996523c0199c61c768d74417b7e96109" +resolved_reference = "08cf609a996523c0199c61c768d74417b7e96109" +subdirectory = "openhands/sdk" + [[package]] name = "openpyxl" version = "3.1.5" @@ -5855,14 +6002,14 @@ ptyprocess = ">=0.5" [[package]] name = "pg8000" -version = "1.31.4" +version = "1.31.5" description = "PostgreSQL interface library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pg8000-1.31.4-py3-none-any.whl", hash = "sha256:d14fb2054642ee80f9a216721892e99e19db60a005358460ffa48872351423d4"}, - {file = "pg8000-1.31.4.tar.gz", hash = "sha256:e7ecce4339891f27b0b22e2f79eb9efe44118bd384207359fc18350f788ace00"}, + {file = "pg8000-1.31.5-py3-none-any.whl", hash = "sha256:0af2c1926b153307639868d2ee5cef6cd3a7d07448e12736989b10e1d491e201"}, + {file = "pg8000-1.31.5.tar.gz", hash = "sha256:46ebb03be52b7a77c03c725c79da2ca281d6e8f59577ca66b17c9009618cae78"}, ] [package.dependencies] @@ -6528,6 +6675,17 @@ files = [ [package.dependencies] pyasn1 = ">=0.6.1,<0.7.0" +[[package]] +name = "pybase62" +version = "1.0.0" +description = "Python module for base62 encoding" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pybase62-1.0.0-py3-none-any.whl", hash = "sha256:60539ad956ec9e9de091bc7ae88c9550bc2fa17f503050cf34d021b75e73cb27"}, +] + [[package]] name = "pycodestyle" version = "2.14.0" @@ -7122,6 +7280,30 @@ PyYAML = "*" docs = ["sphinx"] test = ["mypy", "pyaml", "pytest", "toml", "types-PyYAML", "types-toml"] +[[package]] +name = "python-jose" +version = "3.5.0" +description = "JOSE implementation in Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771"}, + {file = "python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b"}, +] + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"cryptography\""} +ecdsa = "!=0.15" +pyasn1 = ">=0.5.0" +rsa = ">=4.0,<4.1.1 || >4.1.1,<4.4 || >4.4,<5.0" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)"] +test = ["pytest", "pytest-cov"] + [[package]] name = "python-json-logger" version = "3.3.0" diff --git a/enterprise/server/auth/saas_user_auth.py b/enterprise/server/auth/saas_user_auth.py index 4b908767f6f9..456852baf0a8 100644 --- a/enterprise/server/auth/saas_user_auth.py +++ b/enterprise/server/auth/saas_user_auth.py @@ -224,6 +224,16 @@ async def get_instance(cls, request: Request) -> UserAuth: await rate_limiter.hit('auth_uid', user_id) return instance + @classmethod + async def get_for_user(cls, user_id: str) -> UserAuth: + offline_token = await token_manager.load_offline_token(user_id) + assert offline_token is not None + return SaasUserAuth( + user_id=user_id, + refresh_token=SecretStr(offline_token), + auth_type=AuthType.BEARER, + ) + def get_api_key_from_header(request: Request): auth_header = request.headers.get('Authorization') diff --git a/enterprise/server/routes/auth.py b/enterprise/server/routes/auth.py index 5355f150a9ad..e6fa3e725483 100644 --- a/enterprise/server/routes/auth.py +++ b/enterprise/server/routes/auth.py @@ -424,7 +424,7 @@ async def refresh_tokens( provider_handler = ProviderHandler( create_provider_tokens_object([provider]), external_auth_id=user_id ) - service = provider_handler._get_service(provider) + service = provider_handler.get_service(provider) token = await service.get_latest_token() if not token: raise HTTPException( diff --git a/enterprise/server/saas_nested_conversation_manager.py b/enterprise/server/saas_nested_conversation_manager.py index 469b71e33d92..6eb03a66e338 100644 --- a/enterprise/server/saas_nested_conversation_manager.py +++ b/enterprise/server/saas_nested_conversation_manager.py @@ -784,6 +784,7 @@ async def _create_runtime( env_vars['SKIP_DEPENDENCY_CHECK'] = '1' env_vars['INITIAL_NUM_WARM_SERVERS'] = '1' env_vars['INIT_GIT_IN_EMPTY_WORKSPACE'] = '1' + env_vars['ENABLE_V1'] = '0' # We need this for LLM traces tracking to identify the source of the LLM calls env_vars['WEB_HOST'] = WEB_HOST diff --git a/enterprise/server/utils/conversation_callback_utils.py b/enterprise/server/utils/conversation_callback_utils.py index dc36b0c70372..9224e686bf09 100644 --- a/enterprise/server/utils/conversation_callback_utils.py +++ b/enterprise/server/utils/conversation_callback_utils.py @@ -195,14 +195,11 @@ def update_active_working_seconds( file_store: The FileStore instance for accessing conversation data """ try: - # Get all events for the conversation - events = list(event_store.get_events()) - # Track agent state changes and calculate running time running_start_time = None total_running_seconds = 0.0 - for event in events: + for event in event_store.search_events(): if isinstance(event, AgentStateChangedObservation) and event.timestamp: event_timestamp = datetime.fromisoformat(event.timestamp).timestamp() diff --git a/enterprise/storage/base.py b/enterprise/storage/base.py index 6b37477f567d..f462e837e536 100644 --- a/enterprise/storage/base.py +++ b/enterprise/storage/base.py @@ -2,6 +2,6 @@ Unified SQLAlchemy declarative base for all models. """ -from sqlalchemy.orm import declarative_base +from openhands.app_server.utils.sql_utils import Base -Base = declarative_base() +__all__ = ['Base'] diff --git a/enterprise/storage/database.py b/enterprise/storage/database.py index 61e490554f47..f0d8e9d62cbf 100644 --- a/enterprise/storage/database.py +++ b/enterprise/storage/database.py @@ -1,7 +1,6 @@ import asyncio import os -from google.cloud.sql.connector import Connector from sqlalchemy import create_engine from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker @@ -26,6 +25,8 @@ def _get_db_engine(): if GCP_DB_INSTANCE: # GCP environments def get_db_connection(): + from google.cloud.sql.connector import Connector + connector = Connector() instance_string = f'{GCP_PROJECT}:{GCP_REGION}:{GCP_DB_INSTANCE}' return connector.connect( @@ -52,6 +53,8 @@ def get_db_connection(): async def async_creator(): + from google.cloud.sql.connector import Connector + loop = asyncio.get_running_loop() async with Connector(loop=loop) as connector: conn = await connector.connect_async( diff --git a/enterprise/storage/saas_conversation_store.py b/enterprise/storage/saas_conversation_store.py index c0fbda6d9059..63041710d07b 100644 --- a/enterprise/storage/saas_conversation_store.py +++ b/enterprise/storage/saas_conversation_store.py @@ -52,6 +52,14 @@ def _to_external_model(self, conversation_metadata: StoredConversationMetadata): # Convert string to ProviderType enum kwargs['git_provider'] = ProviderType(kwargs['git_provider']) + # Remove V1 attributes + kwargs.pop('max_budget_per_task', None) + kwargs.pop('cache_read_tokens', None) + kwargs.pop('cache_write_tokens', None) + kwargs.pop('reasoning_tokens', None) + kwargs.pop('context_window', None) + kwargs.pop('per_turn_token', None) + return ConversationMetadata(**kwargs) async def save_metadata(self, metadata: ConversationMetadata): diff --git a/enterprise/storage/stored_conversation_metadata.py b/enterprise/storage/stored_conversation_metadata.py index cc289e87d146..aac9f215a84d 100644 --- a/enterprise/storage/stored_conversation_metadata.py +++ b/enterprise/storage/stored_conversation_metadata.py @@ -1,41 +1,8 @@ -import uuid -from datetime import UTC, datetime +from openhands.app_server.app_conversation.sql_app_conversation_info_service import ( + StoredConversationMetadata as _StoredConversationMetadata, +) -from sqlalchemy import JSON, Column, DateTime, Float, Integer, String -from storage.base import Base +StoredConversationMetadata = _StoredConversationMetadata -class StoredConversationMetadata(Base): # type: ignore - __tablename__ = 'conversation_metadata' - conversation_id = Column( - String, primary_key=True, default=lambda: str(uuid.uuid4()) - ) - github_user_id = Column(String, nullable=True) # The GitHub user ID - user_id = Column(String, nullable=False) # The Keycloak User ID - selected_repository = Column(String, nullable=True) - selected_branch = Column(String, nullable=True) - git_provider = Column( - String, nullable=True - ) # The git provider (GitHub, GitLab, etc.) - title = Column(String, nullable=True) - last_updated_at = Column( - DateTime(timezone=True), - default=lambda: datetime.now(UTC), # type: ignore[attr-defined] - ) - created_at = Column( - DateTime(timezone=True), - default=lambda: datetime.now(UTC), # type: ignore[attr-defined] - ) - trigger = Column(String, nullable=True) - pr_number = Column( - JSON, nullable=True - ) # List of PR numbers associated with the conversation - - # Cost and token metrics - accumulated_cost = Column(Float, default=0.0) - prompt_tokens = Column(Integer, default=0) - completion_tokens = Column(Integer, default=0) - total_tokens = Column(Integer, default=0) - - # LLM model used for the conversation - llm_model = Column(String, nullable=True) +__all__ = ['StoredConversationMetadata'] diff --git a/enterprise/tests/unit/integrations/jira/test_jira_view.py b/enterprise/tests/unit/integrations/jira/test_jira_view.py index 0fcdcd8afac0..07b885f59df4 100644 --- a/enterprise/tests/unit/integrations/jira/test_jira_view.py +++ b/enterprise/tests/unit/integrations/jira/test_jira_view.py @@ -137,7 +137,9 @@ async def test_create_or_update_conversation_no_metadata( ): """Test conversation update with no metadata""" mock_store = AsyncMock() - mock_store.get_metadata.return_value = None + mock_store.get_metadata.side_effect = FileNotFoundError( + 'No such file or directory' + ) mock_store_impl.return_value = mock_store with pytest.raises( diff --git a/enterprise/tests/unit/integrations/jira_dc/test_jira_dc_view.py b/enterprise/tests/unit/integrations/jira_dc/test_jira_dc_view.py index 3efb616a62a3..bd1f1f352e03 100644 --- a/enterprise/tests/unit/integrations/jira_dc/test_jira_dc_view.py +++ b/enterprise/tests/unit/integrations/jira_dc/test_jira_dc_view.py @@ -137,7 +137,9 @@ async def test_create_or_update_conversation_no_metadata( ): """Test conversation update with no metadata""" mock_store = AsyncMock() - mock_store.get_metadata.return_value = None + mock_store.get_metadata.side_effect = FileNotFoundError( + 'No such file or directory' + ) mock_store_impl.return_value = mock_store with pytest.raises( diff --git a/enterprise/tests/unit/integrations/linear/test_linear_view.py b/enterprise/tests/unit/integrations/linear/test_linear_view.py index 67acf720f0ef..dc410a9a5c3f 100644 --- a/enterprise/tests/unit/integrations/linear/test_linear_view.py +++ b/enterprise/tests/unit/integrations/linear/test_linear_view.py @@ -137,7 +137,9 @@ async def test_create_or_update_conversation_no_metadata( ): """Test conversation update with no metadata""" mock_store = AsyncMock() - mock_store.get_metadata.return_value = None + mock_store.get_metadata.side_effect = FileNotFoundError( + 'No such file or directory' + ) mock_store_impl.return_value = mock_store with pytest.raises( diff --git a/enterprise/tests/unit/server/test_conversation_callback_utils.py b/enterprise/tests/unit/server/test_conversation_callback_utils.py index 598befe79ac9..128f2d82d2f3 100644 --- a/enterprise/tests/unit/server/test_conversation_callback_utils.py +++ b/enterprise/tests/unit/server/test_conversation_callback_utils.py @@ -80,7 +80,7 @@ def test_update_active_working_seconds_multiple_state_changes( events.append(event6) # Configure the mock event store to return our test events - mock_event_store.get_events.return_value = events + mock_event_store.search_events.return_value = events # Call the function under test with mocked session_maker with patch( @@ -133,7 +133,7 @@ def test_update_active_working_seconds_updates_existing_record( events = [event1, event2] - mock_event_store.get_events.return_value = events + mock_event_store.search_events.return_value = events # Call the function under test with mocked session_maker with patch( @@ -178,7 +178,7 @@ def test_update_active_working_seconds_agent_still_running( events = [event1, event2, event3] # No final state change - agent still running - mock_event_store.get_events.return_value = events + mock_event_store.search_events.return_value = events # Call the function under test with mocked session_maker with patch( @@ -221,7 +221,7 @@ def test_update_active_working_seconds_no_running_states( events = [event1, event2, event3] - mock_event_store.get_events.return_value = events + mock_event_store.search_events.return_value = events # Call the function under test with mocked session_maker with patch( @@ -267,7 +267,7 @@ def test_update_active_working_seconds_mixed_event_types( events = [event1, event2, event3, event4] - mock_event_store.get_events.return_value = events + mock_event_store.search_events.return_value = events # Call the function under test with mocked session_maker with patch( @@ -297,7 +297,7 @@ def test_update_active_working_seconds_handles_exceptions( user_id = 'test_user_error' # Configure the mock to raise an exception - mock_event_store.get_events.side_effect = Exception('Test error') + mock_event_store.search_events.side_effect = Exception('Test error') # Call the function under test update_active_working_seconds( @@ -376,7 +376,7 @@ def test_update_active_working_seconds_complex_state_transitions( event10.timestamp = '1970-01-01T00:00:37.000000' events.append(event10) - mock_event_store.get_events.return_value = events + mock_event_store.search_events.return_value = events # Call the function under test with mocked session_maker with patch( diff --git a/evaluation/benchmarks/mint/tasks/reasoning.py b/evaluation/benchmarks/mint/tasks/reasoning.py index 08cf320c359f..64fb18a6b014 100644 --- a/evaluation/benchmarks/mint/tasks/reasoning.py +++ b/evaluation/benchmarks/mint/tasks/reasoning.py @@ -307,7 +307,7 @@ def extract_answer(self, solution: str) -> Any: # Converting the string answer to a number/list/bool/option try: - prediction = eval(prediction) + prediction = ast.literal_eval(prediction) except Exception: LOGGER.warning( f'[TASK] Failed to convert the answer: {prediction}\n{traceback.format_exc()}' diff --git a/evaluation/benchmarks/multi_swe_bench/scripts/rollout_multi_swegym.sh b/evaluation/benchmarks/multi_swe_bench/scripts/rollout_multi_swegym.sh index 826bdcdfbc7f..ed132432e3b1 100755 --- a/evaluation/benchmarks/multi_swe_bench/scripts/rollout_multi_swegym.sh +++ b/evaluation/benchmarks/multi_swe_bench/scripts/rollout_multi_swegym.sh @@ -111,15 +111,10 @@ for run_idx in $(seq 1 $N_RUNS); do echo "### Evaluating on $OUTPUT_FILE ... ###" OUTPUT_CONFIG_FILE="${OUTPUT_FILE%.jsonl}_config.json" export EVAL_SKIP_BUILD_ERRORS=true - pip install multi-swe-bench --quiet --disable-pip-version-check > /dev/null 2>&1 COMMAND="poetry run python ./evaluation/benchmarks/multi_swe_bench/scripts/eval/update_multi_swe_bench_config.py --input $OUTPUT_FILE --output $OUTPUT_CONFIG_FILE --dataset $EVAL_DATASET; - python -m multi_swe_bench.harness.run_evaluation --config $OUTPUT_CONFIG_FILE + poetry run python -m multi_swe_bench.harness.run_evaluation --config $OUTPUT_CONFIG_FILE " - if [ -n "$EVAL_LIMIT" ]; then - echo "EVAL_LIMIT: $EVAL_LIMIT" - COMMAND="$COMMAND --eval-n-limit $EVAL_LIMIT" - fi echo "Running command: $COMMAND" # Run the command eval $COMMAND diff --git a/evaluation/integration_tests/run_infer.py b/evaluation/integration_tests/run_infer.py index c2ccf54bc9d9..88d49d4055b6 100644 --- a/evaluation/integration_tests/run_infer.py +++ b/evaluation/integration_tests/run_infer.py @@ -24,8 +24,8 @@ from openhands.core.config import ( AgentConfig, OpenHandsConfig, + get_evaluation_parser, get_llm_config_arg, - parse_arguments, ) from openhands.core.logger import openhands_logger as logger from openhands.core.main import create_runtime, run_controller @@ -166,7 +166,8 @@ def load_integration_tests() -> pd.DataFrame: if __name__ == '__main__': - args = parse_arguments() + parser = get_evaluation_parser() + args, _ = parser.parse_known_args() integration_tests = load_integration_tests() llm_config = None diff --git a/frontend/__tests__/utils/map-provider.test.ts b/frontend/__tests__/utils/map-provider.test.ts index 8f5753a8256a..e7311c361d73 100644 --- a/frontend/__tests__/utils/map-provider.test.ts +++ b/frontend/__tests__/utils/map-provider.test.ts @@ -24,4 +24,5 @@ test("mapProvider", () => { expect(mapProvider("replicate")).toBe("Replicate"); expect(mapProvider("voyage")).toBe("Voyage AI"); expect(mapProvider("openrouter")).toBe("OpenRouter"); + expect(mapProvider("clarifai")).toBe("Clarifai"); }); diff --git a/frontend/src/assets/branding/all-hands-logo.svg b/frontend/src/assets/branding/openhands-logo.svg similarity index 100% rename from frontend/src/assets/branding/all-hands-logo.svg rename to frontend/src/assets/branding/openhands-logo.svg diff --git a/frontend/src/components/features/payment/setup-payment-modal.tsx b/frontend/src/components/features/payment/setup-payment-modal.tsx index e62141dd3714..30cb0a4e54c9 100644 --- a/frontend/src/components/features/payment/setup-payment-modal.tsx +++ b/frontend/src/components/features/payment/setup-payment-modal.tsx @@ -1,7 +1,7 @@ import { useMutation } from "@tanstack/react-query"; import { Trans, useTranslation } from "react-i18next"; import { I18nKey } from "#/i18n/declaration"; -import AllHandsLogo from "#/assets/branding/all-hands-logo.svg?react"; +import OpenHandsLogo from "#/assets/branding/openhands-logo.svg?react"; import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop"; import { ModalBody } from "#/components/shared/modals/modal-body"; import BillingService from "#/api/billing-service/billing-service.api"; @@ -23,7 +23,7 @@ export function SetupPaymentModal() { return ( - +

{t(I18nKey.BILLING$YOUVE_GOT_50)} diff --git a/frontend/src/components/features/sidebar/sidebar.tsx b/frontend/src/components/features/sidebar/sidebar.tsx index f8a9c7a77c9b..0580d9db3bc4 100644 --- a/frontend/src/components/features/sidebar/sidebar.tsx +++ b/frontend/src/components/features/sidebar/sidebar.tsx @@ -2,7 +2,7 @@ import React from "react"; import { useLocation } from "react-router"; import { useGitUser } from "#/hooks/query/use-git-user"; import { UserActions } from "./user-actions"; -import { AllHandsLogoButton } from "#/components/shared/buttons/all-hands-logo-button"; +import { OpenHandsLogoButton } from "#/components/shared/buttons/openhands-logo-button"; import { NewProjectButton } from "#/components/shared/buttons/new-project-button"; import { ConversationPanelButton } from "#/components/shared/buttons/conversation-panel-button"; import { SettingsModal } from "#/components/shared/modals/settings/settings-modal"; @@ -74,7 +74,7 @@ export function Sidebar() {