mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
Clarify OpenHands naming (replace “OSS” wording in docs and backend) (#12235)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -150,9 +150,9 @@ Each integration follows a consistent pattern with service classes, storage mode
|
|||||||
|
|
||||||
**Important Notes:**
|
**Important Notes:**
|
||||||
- Enterprise code is licensed under Polyform Free Trial License (30-day limit)
|
- Enterprise code is licensed under Polyform Free Trial License (30-day limit)
|
||||||
- The enterprise server extends the OSS server through dynamic imports
|
- The enterprise server extends the OpenHands server through dynamic imports
|
||||||
- Database changes require careful migration planning in `enterprise/migrations/`
|
- Database changes require careful migration planning in `enterprise/migrations/`
|
||||||
- Always test changes in both OSS and enterprise contexts
|
- Always test changes in both OpenHands and enterprise contexts
|
||||||
- Use the enterprise-specific Makefile commands for development
|
- Use the enterprise-specific Makefile commands for development
|
||||||
|
|
||||||
**Enterprise Testing Best Practices:**
|
**Enterprise Testing Best Practices:**
|
||||||
@@ -166,7 +166,7 @@ Each integration follows a consistent pattern with service classes, storage mode
|
|||||||
**Import Patterns:**
|
**Import Patterns:**
|
||||||
- Use relative imports without `enterprise.` prefix in enterprise code
|
- Use relative imports without `enterprise.` prefix in enterprise code
|
||||||
- Example: `from storage.database import session_maker` not `from enterprise.storage.database import session_maker`
|
- Example: `from storage.database import session_maker` not `from enterprise.storage.database import session_maker`
|
||||||
- This ensures code works in both OSS and enterprise contexts
|
- This ensures code works in both OpenHands and enterprise contexts
|
||||||
|
|
||||||
**Test Structure:**
|
**Test Structure:**
|
||||||
- Place tests in `enterprise/tests/unit/` following the same structure as the source code
|
- Place tests in `enterprise/tests/unit/` following the same structure as the source code
|
||||||
|
|||||||
@@ -10,6 +10,15 @@ repos:
|
|||||||
args: ["--allow-multiple-documents"]
|
args: ["--allow-multiple-documents"]
|
||||||
- id: debug-statements
|
- id: debug-statements
|
||||||
|
|
||||||
|
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: warn-appmode-oss
|
||||||
|
name: "Warn on AppMode.OSS in backend (use AppMode.OPENHANDS)"
|
||||||
|
language: system
|
||||||
|
entry: bash -lc 'if rg -n "\\bAppMode\\.OSS\\b" openhands tests/unit; then echo "Found AppMode.OSS usage. Prefer AppMode.OPENHANDS."; exit 1; fi'
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
rev: v2.5.1
|
rev: v2.5.1
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ This directory contains the enterprise server used by [OpenHands Cloud](https://
|
|||||||
|
|
||||||
You may also want to check out the MIT-licensed [OpenHands](https://github.com/OpenHands/OpenHands)
|
You may also want to check out the MIT-licensed [OpenHands](https://github.com/OpenHands/OpenHands)
|
||||||
|
|
||||||
## Extension of OpenHands (OSS)
|
## Extension of OpenHands
|
||||||
|
|
||||||
The code in `/enterprise` directory builds on top of open source (OSS) code, extending its functionality. The enterprise code is entangled with the OSS code in two ways
|
The code in `/enterprise` builds on top of OpenHands (MIT-licensed), extending its functionality. The enterprise code is entangled with OpenHands in two ways:
|
||||||
|
|
||||||
- Enterprise stacks on top of OSS. For example, the middleware in enterprise is stacked right on top of the middlewares in OSS. In `SAAS`, the middleware from BOTH repos will be present and running (which can sometimes cause conflicts)
|
- Enterprise stacks on top of OpenHands. For example, the middleware in enterprise is stacked right on top of the middlewares in OpenHands. In `SAAS`, the middleware from BOTH repos will be present and running (which can sometimes cause conflicts)
|
||||||
|
|
||||||
- Enterprise overrides the implementation in OSS (only one is present at a time). For example, the server config SaasServerConfig which overrides [`ServerConfig`](https://github.com/OpenHands/OpenHands/blob/main/openhands/server/config/server_config.py#L8) on OSS. This is done through dynamic imports ([see here](https://github.com/OpenHands/OpenHands/blob/main/openhands/server/config/server_config.py#L37-#L45))
|
- Enterprise overrides the implementation in OpenHands (only one is present at a time). For example, the server config SaasServerConfig overrides [`ServerConfig`](https://github.com/OpenHands/OpenHands/blob/main/openhands/server/config/server_config.py#L8) in OpenHands. This is done through dynamic imports ([see here](https://github.com/OpenHands/OpenHands/blob/main/openhands/server/config/server_config.py#L37-#L45))
|
||||||
|
|
||||||
Key areas that change on `SAAS` are
|
Key areas that change on `SAAS` are
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ Key areas that change on `SAAS` are
|
|||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
| Aspect | OSS | Enterprise |
|
| Aspect | OpenHands | Enterprise |
|
||||||
| ------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **Authentication Method** | User adds a personal access token (PAT) through the UI | User performs OAuth through the UI. The Github app provides a short-lived access token and refresh token |
|
| **Authentication Method** | User adds a personal access token (PAT) through the UI | User performs OAuth through the UI. The GitHub app provides a short-lived access token and refresh token |
|
||||||
| **Token Storage** | PAT is stored in **Settings** | Token is stored in **GithubTokenManager** (a file store in our backend) |
|
| **Token Storage** | PAT is stored in **Settings** | Token is stored in **GithubTokenManager** (a file store in our backend) |
|
||||||
| **Authenticated status** | We simply check if token exists in `Settings` | We issue a signed cookie with `github_user_id` during oauth, so subsequent requests with the cookie can be considered authenticated |
|
| **Authenticated status** | We simply check if token exists in `Settings` | We issue a signed cookie with `github_user_id` during OAuth, so subsequent requests with the cookie can be considered authenticated |
|
||||||
|
|
||||||
Note that in the future, authentication will happen via keycloak. All modifications for authentication will happen in enterprise.
|
Note that in the future, authentication will happen via keycloak. All modifications for authentication will happen in enterprise.
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ Note that in the future, authentication will happen via keycloak. All modificati
|
|||||||
|
|
||||||
The github service is responsible for interacting with Github APIs. As a consequence, it uses the user's token and refreshes it if need be
|
The github service is responsible for interacting with Github APIs. As a consequence, it uses the user's token and refreshes it if need be
|
||||||
|
|
||||||
| Aspect | OSS | Enterprise |
|
| Aspect | OpenHands | Enterprise |
|
||||||
| ------------------------- | -------------------------------------- | ---------------------------------------------- |
|
| ------------------------- | -------------------------------------- | ---------------------------------------------- |
|
||||||
| **Class used** | `GitHubService` | `SaaSGitHubService` |
|
| **Class used** | `GitHubService` | `SaaSGitHubService` |
|
||||||
| **Token used** | User's PAT fetched from `Settings` | User's token fetched from `GitHubTokenManager` |
|
| **Token used** | User's PAT fetched from `Settings` | User's token fetched from `GitHubTokenManager` |
|
||||||
@@ -50,7 +50,7 @@ NOTE: in the future we will simply replace the `GithubTokenManager` with keycloa
|
|||||||
|
|
||||||
## User ID vs User Token
|
## User ID vs User Token
|
||||||
|
|
||||||
- On OSS, the entire APP revolves around the Github token the user sets. `openhands/server` uses `request.state.github_token` for the entire app
|
- In OpenHands, the entire app revolves around the GitHub token the user sets. `openhands/server` uses `request.state.github_token` for the entire app
|
||||||
- On Enterprise, the entire APP resolves around the Github User ID. This is because the cookie sets it, so `openhands/server` AND `enterprise/server` depend on it and completly ignore `request.state.github_token` (token is fetched from `GithubTokenManager` instead)
|
- On Enterprise, the entire APP resolves around the Github User ID. This is because the cookie sets it, so `openhands/server` AND `enterprise/server` depend on it and completly ignore `request.state.github_token` (token is fetched from `GithubTokenManager` instead)
|
||||||
|
|
||||||
Note that introducing Github User ID on OSS, for instance, will cause large breakages.
|
Note that introducing GitHub User ID in OpenHands, for instance, will cause large breakages.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
You have a few options here, which are expanded on below:
|
You have a few options here, which are expanded on below:
|
||||||
|
|
||||||
- A simple local development setup, with live reloading for both OSS and this repo
|
- A simple local development setup, with live reloading for both OpenHands and this repo
|
||||||
- A more complex setup that includes Redis
|
- A more complex setup that includes Redis
|
||||||
- An even more complex setup that includes GitHub events
|
- An even more complex setup that includes GitHub events
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ Before starting, make sure you have the following tools installed:
|
|||||||
|
|
||||||
## Option 1: Simple local development
|
## Option 1: Simple local development
|
||||||
|
|
||||||
This option will allow you to modify the both the OSS code and the code in this repo,
|
This option will allow you to modify both the OpenHands code and the code in this repo,
|
||||||
and see the changes in real-time.
|
and see the changes in real-time.
|
||||||
|
|
||||||
This option works best for most scenarios. The only thing it's missing is
|
This option works best for most scenarios. The only thing it's missing is
|
||||||
@@ -105,7 +105,7 @@ export REDIS_PORT=6379
|
|||||||
|
|
||||||
(see above)
|
(see above)
|
||||||
|
|
||||||
### 2. Build OSS Openhands
|
### 2. Build OpenHands
|
||||||
|
|
||||||
Develop on [Openhands](https://github.com/All-Hands-AI/OpenHands) locally. When ready, run the following inside Openhands repo (not the Deploy repo)
|
Develop on [Openhands](https://github.com/All-Hands-AI/OpenHands) locally. When ready, run the following inside Openhands repo (not the Deploy repo)
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ Visit the tunnel domain found in Step 4 to run the app (`https://bc71-2603-7000-
|
|||||||
|
|
||||||
### Local Debugging with VSCode
|
### Local Debugging with VSCode
|
||||||
|
|
||||||
Local Development necessitates running a version of OpenHands that is as similar as possible to the version running in the SAAS Environment. Before running these steps, it is assumed you have a local development version of the OSS OpenHands project running.
|
Local Development necessitates running a version of OpenHands that is as similar as possible to the version running in the SAAS Environment. Before running these steps, it is assumed you have a local development version of OpenHands running.
|
||||||
|
|
||||||
#### Redis
|
#### Redis
|
||||||
|
|
||||||
@@ -201,8 +201,8 @@ And then invoking `printenv`. NOTE: _DO NOT DO THIS WITH PROD!!!_ (Hopefully by
|
|||||||
"DEBUG": "1",
|
"DEBUG": "1",
|
||||||
"FILE_STORE": "local",
|
"FILE_STORE": "local",
|
||||||
"REDIS_HOST": "localhost:6379",
|
"REDIS_HOST": "localhost:6379",
|
||||||
"OPENHANDS": "<YOUR LOCAL OSS OPENHANDS DIR>",
|
"OPENHANDS": "<YOUR LOCAL OPENHANDS DIR>",
|
||||||
"FRONTEND_DIRECTORY": "<YOUR LOCAL OSS OPENHANDS DIR>/frontend/build",
|
"FRONTEND_DIRECTORY": "<YOUR LOCAL OPENHANDS DIR>/frontend/build",
|
||||||
"SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/openhands/runtime:main-nikolaik",
|
"SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/openhands/runtime:main-nikolaik",
|
||||||
"FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state",
|
"FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state",
|
||||||
"OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig",
|
"OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig",
|
||||||
@@ -235,8 +235,8 @@ And then invoking `printenv`. NOTE: _DO NOT DO THIS WITH PROD!!!_ (Hopefully by
|
|||||||
"DEBUG": "1",
|
"DEBUG": "1",
|
||||||
"FILE_STORE": "local",
|
"FILE_STORE": "local",
|
||||||
"REDIS_HOST": "localhost:6379",
|
"REDIS_HOST": "localhost:6379",
|
||||||
"OPENHANDS": "<YOUR LOCAL OSS OPENHANDS DIR>",
|
"OPENHANDS": "<YOUR LOCAL OPENHANDS DIR>",
|
||||||
"FRONTEND_DIRECTORY": "<YOUR LOCAL OSS OPENHANDS DIR>/frontend/build",
|
"FRONTEND_DIRECTORY": "<YOUR LOCAL OPENHANDS DIR>/frontend/build",
|
||||||
"SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/openhands/runtime:main-nikolaik",
|
"SANDBOX_RUNTIME_CONTAINER_IMAGE": "ghcr.io/openhands/runtime:main-nikolaik",
|
||||||
"FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state",
|
"FILE_STORE_PATH": "<YOUR HOME DIRECTORY>>/.openhands-state",
|
||||||
"OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig",
|
"OPENHANDS_CONFIG_CLS": "server.config.SaaSServerConfig",
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ npm run dev:mock:saas
|
|||||||
These commands set `VITE_MOCK_API=true` which activates the MSW Service Worker to intercept requests.
|
These commands set `VITE_MOCK_API=true` which activates the MSW Service Worker to intercept requests.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> **OSS vs SaaS Mode**
|
> **OpenHands vs SaaS Mode**
|
||||||
>
|
>
|
||||||
> OpenHands runs in two modes:
|
> OpenHands runs in two modes:
|
||||||
> - **OSS mode**: For local/self-hosted deployments where users provide their own LLM API keys and configure git providers manually
|
> - **OpenHands mode**: For local/self-hosted deployments where users provide their own LLM API keys and configure git providers manually
|
||||||
> - **SaaS mode**: For the cloud offering with billing, managed API keys, and OAuth-based GitHub integration
|
> - **SaaS mode**: For the cloud offering with billing, managed API keys, and OAuth-based GitHub integration
|
||||||
>
|
>
|
||||||
> Use `dev:mock:saas` when working on SaaS-specific features like billing, API key management, or subscription flows.
|
> Use `dev:mock:saas` when working on SaaS-specific features like billing, API key management, or subscription flows.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Sandboxed Conversation router for OpenHands Server."""
|
"""Sandboxed Conversation router for OpenHands App Server."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ def get_openhands_provider_base_url() -> str | None:
|
|||||||
|
|
||||||
def _get_default_lifespan():
|
def _get_default_lifespan():
|
||||||
# Check legacy parameters for saas mode. If we are in SAAS mode do not apply
|
# Check legacy parameters for saas mode. If we are in SAAS mode do not apply
|
||||||
# OSS alembic migrations
|
# OpenHands alembic migrations
|
||||||
if 'saas' in (os.getenv('OPENHANDS_CONFIG_CLS') or '').lower():
|
if 'saas' in (os.getenv('OPENHANDS_CONFIG_CLS') or '').lower():
|
||||||
return None
|
return None
|
||||||
return OssAppLifespanService()
|
return OssAppLifespanService()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Event router for OpenHands Server."""
|
"""Event router for OpenHands App Server."""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Event Callback router for OpenHands Server."""
|
"""Event Callback router for OpenHands App Server."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import importlib
|
import importlib
|
||||||
@@ -188,7 +188,7 @@ async def get_secret(
|
|||||||
if user_id:
|
if user_id:
|
||||||
user_auth = await get_user_auth_for_user(user_id)
|
user_auth = await get_user_auth_for_user(user_id)
|
||||||
else:
|
else:
|
||||||
# OSS mode - use default user auth
|
# OpenHands (OSS mode) - use default user auth
|
||||||
user_auth = DefaultUserAuth()
|
user_auth = DefaultUserAuth()
|
||||||
|
|
||||||
# Create UserContext directly
|
# Create UserContext directly
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Runtime Containers router for OpenHands Server."""
|
"""Runtime Containers router for OpenHands App Server."""
|
||||||
|
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Runtime Images router for OpenHands Server."""
|
"""Runtime Images router for OpenHands App Server."""
|
||||||
|
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Database configuration and session management for OpenHands Server."""
|
"""Database configuration and session management for OpenHands App Server."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class AuthUserContext(UserContext):
|
|||||||
|
|
||||||
async def get_user_id(self) -> str | None:
|
async def get_user_id(self) -> str | None:
|
||||||
# If you have an auth object here you are logged in. If user_id is None
|
# If you have an auth object here you are logged in. If user_id is None
|
||||||
# it means we are in OSS mode.
|
# it means we are in OpenHands (OSS mode).
|
||||||
user_id = await self.user_auth.get_user_id()
|
user_id = await self.user_auth.get_user_id()
|
||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""User router for OpenHands Server. For the moment, this simply implements the /me endpoint."""
|
"""User router for OpenHands App Server. For the moment, this simply implements the /me endpoint."""
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, status
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class AzureDevOpsReposMixin(AzureDevOpsMixinBase):
|
|||||||
sort: str = 'updated',
|
sort: str = 'updated',
|
||||||
order: str = 'desc',
|
order: str = 'desc',
|
||||||
public: bool = False,
|
public: bool = False,
|
||||||
app_mode: AppMode = AppMode.OSS,
|
app_mode: AppMode = AppMode.OPENHANDS,
|
||||||
) -> list[Repository]:
|
) -> list[Repository]:
|
||||||
"""Search for repositories in Azure DevOps."""
|
"""Search for repositories in Azure DevOps."""
|
||||||
# Get all repositories across all projects in the organization
|
# Get all repositories across all projects in the organization
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class GitLabReposMixin(GitLabMixinBase):
|
|||||||
sort: str = 'updated',
|
sort: str = 'updated',
|
||||||
order: str = 'desc',
|
order: str = 'desc',
|
||||||
public: bool = False,
|
public: bool = False,
|
||||||
app_mode: AppMode = AppMode.OSS,
|
app_mode: AppMode = AppMode.OPENHANDS,
|
||||||
) -> list[Repository]:
|
) -> list[Repository]:
|
||||||
if public:
|
if public:
|
||||||
# When public=True, query is a GitLab URL that we need to parse
|
# When public=True, query is a GitLab URL that we need to parse
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ app.include_router(conversation_api_router)
|
|||||||
app.include_router(manage_conversation_api_router)
|
app.include_router(manage_conversation_api_router)
|
||||||
app.include_router(settings_router)
|
app.include_router(settings_router)
|
||||||
app.include_router(secrets_router)
|
app.include_router(secrets_router)
|
||||||
if server_config.app_mode == AppMode.OSS:
|
if server_config.app_mode == AppMode.OPENHANDS:
|
||||||
app.include_router(git_api_router)
|
app.include_router(git_api_router)
|
||||||
if server_config.enable_v1:
|
if server_config.enable_v1:
|
||||||
app.include_router(v1_router.router)
|
app.include_router(v1_router.router)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from openhands.utils.import_utils import get_impl
|
|||||||
|
|
||||||
class ServerConfig(ServerConfigInterface):
|
class ServerConfig(ServerConfigInterface):
|
||||||
config_cls = os.environ.get('OPENHANDS_CONFIG_CLS', None)
|
config_cls = os.environ.get('OPENHANDS_CONFIG_CLS', None)
|
||||||
app_mode = AppMode.OSS
|
app_mode = AppMode.OPENHANDS
|
||||||
posthog_client_key = 'phc_3ESMmY9SgqEAGBB6sMGK5ayYHkeUuknH2vP6FmWH9RA'
|
posthog_client_key = 'phc_3ESMmY9SgqEAGBB6sMGK5ayYHkeUuknH2vP6FmWH9RA'
|
||||||
github_client_id = os.environ.get('GITHUB_APP_CLIENT_ID', '')
|
github_client_id = os.environ.get('GITHUB_APP_CLIENT_ID', '')
|
||||||
enable_billing = os.environ.get('ENABLE_BILLING', 'false') == 'true'
|
enable_billing = os.environ.get('ENABLE_BILLING', 'false') == 'true'
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ from typing import Any, ClassVar, Protocol
|
|||||||
|
|
||||||
|
|
||||||
class AppMode(Enum):
|
class AppMode(Enum):
|
||||||
OSS = 'oss'
|
OPENHANDS = 'oss'
|
||||||
SAAS = 'saas'
|
SAAS = 'saas'
|
||||||
|
|
||||||
|
# Backwards-compatible alias (deprecated): prefer AppMode.OPENHANDS
|
||||||
|
OSS = 'oss'
|
||||||
|
|
||||||
|
|
||||||
class SessionMiddlewareInterface(Protocol):
|
class SessionMiddlewareInterface(Protocol):
|
||||||
"""Protocol for session middleware classes."""
|
"""Protocol for session middleware classes."""
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ async def test_search_repositories_url_parsing_standard_url(bitbucket_service):
|
|||||||
sort='updated',
|
sort='updated',
|
||||||
order='desc',
|
order='desc',
|
||||||
public=True,
|
public=True,
|
||||||
app_mode=AppMode.OSS,
|
app_mode=AppMode.OPENHANDS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify the correct workspace/repo combination was extracted and passed
|
# Verify the correct workspace/repo combination was extracted and passed
|
||||||
@@ -81,7 +81,7 @@ async def test_search_repositories_url_parsing_with_extra_path_segments(
|
|||||||
sort='updated',
|
sort='updated',
|
||||||
order='desc',
|
order='desc',
|
||||||
public=True,
|
public=True,
|
||||||
app_mode=AppMode.OSS,
|
app_mode=AppMode.OPENHANDS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify the correct workspace/repo combination was extracted from complex URL
|
# Verify the correct workspace/repo combination was extracted from complex URL
|
||||||
@@ -103,7 +103,7 @@ async def test_search_repositories_url_parsing_invalid_url(bitbucket_service):
|
|||||||
sort='updated',
|
sort='updated',
|
||||||
order='desc',
|
order='desc',
|
||||||
public=True,
|
public=True,
|
||||||
app_mode=AppMode.OSS,
|
app_mode=AppMode.OPENHANDS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Should return empty list for invalid URL and not call API
|
# Should return empty list for invalid URL and not call API
|
||||||
@@ -126,7 +126,7 @@ async def test_search_repositories_url_parsing_insufficient_path_segments(
|
|||||||
sort='updated',
|
sort='updated',
|
||||||
order='desc',
|
order='desc',
|
||||||
public=True,
|
public=True,
|
||||||
app_mode=AppMode.OSS,
|
app_mode=AppMode.OPENHANDS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Should return empty list for insufficient path segments and not call API
|
# Should return empty list for insufficient path segments and not call API
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ async def test_get_conversation_link_non_saas_mode():
|
|||||||
|
|
||||||
# Test with non-SAAS mode
|
# Test with non-SAAS mode
|
||||||
with patch('openhands.server.routes.mcp.server_config') as mock_config:
|
with patch('openhands.server.routes.mcp.server_config') as mock_config:
|
||||||
mock_config.app_mode = AppMode.OSS
|
mock_config.app_mode = AppMode.OPENHANDS
|
||||||
|
|
||||||
# Call the function
|
# Call the function
|
||||||
result = await get_conversation_link(
|
result = await get_conversation_link(
|
||||||
|
|||||||
@@ -102,9 +102,9 @@ async def test_setup_with_provided_tokens_uses_real_tokens(
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_setup_without_tokens_non_saas_uses_user_secrets(mock_settings):
|
async def test_setup_without_tokens_non_saas_uses_user_secrets(mock_settings):
|
||||||
"""Test that OSS mode uses user_secrets.provider_tokens when no tokens provided.
|
"""Test that OpenHands (OSS mode) uses user_secrets.provider_tokens when no tokens provided.
|
||||||
|
|
||||||
This test verifies OSS mode backward compatibility - tokens come from local config, not endpoint.
|
This test verifies OpenHands (OSS mode) backward compatibility - tokens come from local config, not endpoint.
|
||||||
"""
|
"""
|
||||||
user_id = 'test_user_456'
|
user_id = 'test_user_456'
|
||||||
conversation_id = 'test_conv_123'
|
conversation_id = 'test_conv_123'
|
||||||
@@ -140,7 +140,7 @@ async def test_setup_without_tokens_non_saas_uses_user_secrets(mock_settings):
|
|||||||
mock_secrets_store.load = AsyncMock(return_value=mock_user_secrets)
|
mock_secrets_store.load = AsyncMock(return_value=mock_user_secrets)
|
||||||
mock_secrets_store_cls.return_value = mock_secrets_store
|
mock_secrets_store_cls.return_value = mock_secrets_store
|
||||||
|
|
||||||
mock_server_config.app_mode = AppMode.OSS
|
mock_server_config.app_mode = AppMode.OPENHANDS
|
||||||
|
|
||||||
# Call without endpoint tokens
|
# Call without endpoint tokens
|
||||||
result = await setup_init_conversation_settings(
|
result = await setup_init_conversation_settings(
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ async def test_search_repositories(forgejo_service):
|
|||||||
|
|
||||||
# Call the method
|
# Call the method
|
||||||
repos = await forgejo_service.search_repositories(
|
repos = await forgejo_service.search_repositories(
|
||||||
'test', 10, 'updated', 'desc', public=False, app_mode=AppMode.OSS
|
'test', 10, 'updated', 'desc', public=False, app_mode=AppMode.OPENHANDS
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify the result
|
# Verify the result
|
||||||
@@ -139,7 +139,7 @@ async def test_get_all_repositories(forgejo_service):
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Call the method
|
# Call the method
|
||||||
repos = await forgejo_service.get_all_repositories('updated', AppMode.OSS)
|
repos = await forgejo_service.get_all_repositories('updated', AppMode.OPENHANDS)
|
||||||
|
|
||||||
# Verify the result
|
# Verify the result
|
||||||
assert len(repos) == 3
|
assert len(repos) == 3
|
||||||
|
|||||||
Reference in New Issue
Block a user