diff --git a/docs/generate-swagger-ui.js b/docs/generate-swagger-ui.js index a3e37aab0e..6f546f5b77 100644 --- a/docs/generate-swagger-ui.js +++ b/docs/generate-swagger-ui.js @@ -4,7 +4,7 @@ const swaggerUiDist = require('swagger-ui-dist'); /** * This script manually sets up Swagger UI for the Docusaurus documentation. - * + * * Why we need this approach: * 1. Docusaurus doesn't have a built-in way to integrate Swagger UI * 2. We need to copy the necessary files from swagger-ui-dist to our static directory @@ -26,15 +26,15 @@ const files = fs.readdirSync(swaggerUiDistPath); files.forEach(file => { const sourcePath = path.join(swaggerUiDistPath, file); const targetPath = path.join(targetDir, file); - + // Skip directories and non-essential files - if (fs.statSync(sourcePath).isDirectory() || - file === 'package.json' || + if (fs.statSync(sourcePath).isDirectory() || + file === 'package.json' || file === 'README.md' || file.endsWith('.map')) { return; } - + fs.copyFileSync(sourcePath, targetPath); }); @@ -54,13 +54,13 @@ const indexHtml = ` overflow: -moz-scrollbars-vertical; overflow-y: scroll; } - + *, *:before, *:after { box-sizing: inherit; } - + body { margin: 0; background: #fafafa; @@ -99,4 +99,4 @@ const indexHtml = ` fs.writeFileSync(path.join(targetDir, 'index.html'), indexHtml); -console.log('Swagger UI files generated successfully in static/swagger-ui/'); \ No newline at end of file +console.log('Swagger UI files generated successfully in static/swagger-ui/'); diff --git a/docs/modules/usage/customization/repository.md b/docs/modules/usage/customization/repository.md index 98612c7203..62306632eb 100644 --- a/docs/modules/usage/customization/repository.md +++ b/docs/modules/usage/customization/repository.md @@ -5,7 +5,7 @@ You can customize how OpenHands interacts with your repository by creating a ## Microagents -Microagents allow you to extend OpenHands prompts with information specific to your project and define how OpenHands +Microagents allow you to extend OpenHands prompts with information specific to your project and define how OpenHands should function. See [Microagents Overview](../prompting/microagents-overview) for more information. diff --git a/docs/modules/usage/prompting/microagents-keyword.md b/docs/modules/usage/prompting/microagents-keyword.md index 944b7b47c3..d5b6e31b82 100644 --- a/docs/modules/usage/prompting/microagents-keyword.md +++ b/docs/modules/usage/prompting/microagents-keyword.md @@ -2,7 +2,7 @@ ## Purpose -Keyword-triggered microagents provide OpenHands with specific instructions that are activated when certain keywords +Keyword-triggered microagents provide OpenHands with specific instructions that are activated when certain keywords appear in the prompt. This is useful for tailoring behavior based on particular tools, languages, or frameworks. ## Microagent File @@ -12,7 +12,7 @@ that activate only for prompts with specific keywords. ## Frontmatter Syntax -Frontmatter is required for keyword-triggered microagents. It must be placed at the top of the file, +Frontmatter is required for keyword-triggered microagents. It must be placed at the top of the file, above the guidelines. Enclose the frontmatter in triple dashes (---) and include the following fields: @@ -22,7 +22,7 @@ Enclose the frontmatter in triple dashes (---) and include the following fields: | `name` | A unique identifier for the microagent. | Yes | 'default' | | `type` | Type of microagent. Must be set to `knowledge`. | Yes | 'repo' | | `triggers` | A list of keywords that activate the microagent. | Yes | None | -| `agent` | The agent this microagent applies to. | No | 'CodeActAgent' | +| `agent` | The agent this microagent applies to. | No | 'CodeActAgent' | ## Example @@ -46,4 +46,4 @@ Keyword-triggered microagents: - Apply their specialized knowledge and capabilities. - Follow defined guidelines and restrictions. -[See examples of microagents triggered by keywords in the official OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge) \ No newline at end of file +[See examples of microagents triggered by keywords in the official OpenHands repository](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge) diff --git a/docs/modules/usage/prompting/microagents-overview.md b/docs/modules/usage/prompting/microagents-overview.md index 24e0441b6d..1af648ce93 100644 --- a/docs/modules/usage/prompting/microagents-overview.md +++ b/docs/modules/usage/prompting/microagents-overview.md @@ -1,6 +1,6 @@ # Microagents Overview -Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge. +Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge. They provide expert guidance, automate common tasks, and ensure consistent practices across projects. ## Microagent Types @@ -10,11 +10,11 @@ Currently OpenHands supports the following types of microagents: - [General Repository Microagents](./microagents-repo): General guidelines for OpenHands about the repository. - [Keyword-Triggered Microagents](./microagents-keyword): Guidelines activated by specific keywords in prompts. -To customize OpenHands' behavior, create a .openhands/microagents/ directory in the root of your repository and +To customize OpenHands' behavior, create a .openhands/microagents/ directory in the root of your repository and add `.md` files inside. :::note -Loaded microagents take up space in the context window. +Loaded microagents take up space in the context window. These microagents, alongside user messages, inform OpenHands about the task and the environment. ::: @@ -31,7 +31,7 @@ some-repository/ ## Microagents Frontmatter Requirements -Each microagent file may include frontmatter that provides additional information. In some cases, this frontmatter +Each microagent file may include frontmatter that provides additional information. In some cases, this frontmatter is required: | Microagent Type | Frontmatter Requirement | diff --git a/docs/modules/usage/prompting/microagents-repo.md b/docs/modules/usage/prompting/microagents-repo.md index 8dd7d0f18d..0828011649 100644 --- a/docs/modules/usage/prompting/microagents-repo.md +++ b/docs/modules/usage/prompting/microagents-repo.md @@ -6,13 +6,13 @@ General guidelines for OpenHands to work more effectively with the repository. ## Microagent File -Create a general repository microagent (example: `.openhands/microagents/repo.md`) to include -project-specific instructions, team practices, coding standards, and architectural guidelines that are relevant for +Create a general repository microagent (example: `.openhands/microagents/repo.md`) to include +project-specific instructions, team practices, coding standards, and architectural guidelines that are relevant for **all** prompts in that repository. ## Frontmatter Syntax -The frontmatter for this type of microagent is optional, unless you plan to include more than one general +The frontmatter for this type of microagent is optional, unless you plan to include more than one general repository microagent. Frontmatter should be enclosed in triple dashes (---) and may include the following fields: diff --git a/docs/sidebars.ts b/docs/sidebars.ts index a9a8867957..2d346c8596 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -268,4 +268,4 @@ const sidebars: SidebarsConfig = { ], }; -export default sidebars; \ No newline at end of file +export default sidebars; diff --git a/docs/static/README.md b/docs/static/README.md index b904b511c5..401fbb2bb2 100644 --- a/docs/static/README.md +++ b/docs/static/README.md @@ -12,4 +12,4 @@ This file is used by the Swagger UI interface, which is accessible at `/swagger- The OpenAPI specification is placed in the static directory so that it's accessible at a predictable URL in the deployed site. This allows the Swagger UI to reference it directly. -We only need one copy of the OpenAPI spec file, which is this one in the static directory. \ No newline at end of file +We only need one copy of the OpenAPI spec file, which is this one in the static directory. diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts index 6392e065c8..238b028c4f 100644 --- a/frontend/src/i18n/declaration.ts +++ b/frontend/src/i18n/declaration.ts @@ -35,6 +35,10 @@ export enum I18nKey { CONVERSATION$START_NEW = "CONVERSATION$START_NEW", ACCOUNT_SETTINGS$TITLE = "ACCOUNT_SETTINGS$TITLE", WORKSPACE$TERMINAL_TAB_LABEL = "WORKSPACE$TERMINAL_TAB_LABEL", + WORKSPACE$BROWSER_TAB_LABEL = "WORKSPACE$BROWSER_TAB_LABEL", + WORKSPACE$JUPYTER_TAB_LABEL = "WORKSPACE$JUPYTER_TAB_LABEL", + WORKSPACE$CODE_EDITOR_TAB_LABEL = "WORKSPACE$CODE_EDITOR_TAB_LABEL", + WORKSPACE$TITLE = "WORKSPACE$TITLE", TERMINAL$WAITING_FOR_CLIENT = "TERMINAL$WAITING_FOR_CLIENT", CODE_EDITOR$FILE_SAVED_SUCCESSFULLY = "CODE_EDITOR$FILE_SAVED_SUCCESSFULLY", CODE_EDITOR$SAVING_LABEL = "CODE_EDITOR$SAVING_LABEL", @@ -281,6 +285,7 @@ export enum I18nKey { BUTTON$CREATE = "BUTTON$CREATE", BUTTON$DELETE = "BUTTON$DELETE", BUTTON$COPY_TO_CLIPBOARD = "BUTTON$COPY_TO_CLIPBOARD", + BUTTON$REFRESH = "BUTTON$REFRESH", ERROR$REQUIRED_FIELD = "ERROR$REQUIRED_FIELD", PLANNER$EMPTY_MESSAGE = "PLANNER$EMPTY_MESSAGE", FEEDBACK$PUBLIC_LABEL = "FEEDBACK$PUBLIC_LABEL", diff --git a/openhands/events/serialization/event.py b/openhands/events/serialization/event.py index f18a0d77aa..546bc7155f 100644 --- a/openhands/events/serialization/event.py +++ b/openhands/events/serialization/event.py @@ -5,7 +5,6 @@ from typing import Any from pydantic import BaseModel -from openhands.core.logger import openhands_logger as logger from openhands.events import Event, EventSource from openhands.events.serialization.action import action_from_dict from openhands.events.serialization.observation import observation_from_dict diff --git a/openhands/integrations/github/github_service.py b/openhands/integrations/github/github_service.py index f222b7021e..3b8fa76bbf 100644 --- a/openhands/integrations/github/github_service.py +++ b/openhands/integrations/github/github_service.py @@ -1,5 +1,6 @@ import json import os +from datetime import datetime from typing import Any import httpx @@ -18,8 +19,6 @@ from openhands.integrations.service_types import ( ) from openhands.server.types import AppMode from openhands.utils.import_utils import get_impl -from datetime import datetime - class GitHubService(BaseGitService, GitService): @@ -159,12 +158,9 @@ class GitHubService(BaseGitService, GitService): return repos[:max_repos] # Trim to max_repos if needed - def parse_pushed_at_date(self, repo): - ts = repo.get("pushed_at") - return datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ") if ts else datetime.min - - + ts = repo.get('pushed_at') + return datetime.strptime(ts, '%Y-%m-%dT%H:%M:%SZ') if ts else datetime.min async def get_repositories(self, sort: str, app_mode: AppMode) -> list[Repository]: MAX_REPOS = 1000 @@ -192,11 +188,9 @@ class GitHubService(BaseGitService, GitService): # If we've already reached MAX_REPOS, no need to check other installations if len(all_repos) >= MAX_REPOS: break - - if sort == "pushed": - all_repos.sort( - key=self.parse_pushed_at_date, reverse=True - ) + + if sort == 'pushed': + all_repos.sort(key=self.parse_pushed_at_date, reverse=True) else: # Original behavior for non-SaaS mode params = {'per_page': str(PER_PAGE), 'sort': sort} @@ -205,7 +199,6 @@ class GitHubService(BaseGitService, GitService): # Fetch user repositories all_repos = await self._fetch_paginated_repos(url, params, MAX_REPOS) - # Convert to Repository objects return [ Repository( diff --git a/openhands/integrations/gitlab/gitlab_service.py b/openhands/integrations/gitlab/gitlab_service.py index 20df93fd18..01a2dc402c 100644 --- a/openhands/integrations/gitlab/gitlab_service.py +++ b/openhands/integrations/gitlab/gitlab_service.py @@ -108,7 +108,9 @@ class GitLabService(BaseGitService, GitService): except httpx.HTTPError as e: raise self.handle_http_error(e) - async def execute_graphql_query(self, query: str, variables: dict[str, Any] = {}) -> Any: + async def execute_graphql_query( + self, query: str, variables: dict[str, Any]|None = None + ) -> Any: """ Execute a GraphQL query against the GitLab GraphQL API @@ -119,6 +121,8 @@ class GitLabService(BaseGitService, GitService): Returns: The data portion of the GraphQL response """ + if variables is None: + variables = {} try: async with httpx.AsyncClient() as client: gitlab_headers = await self._get_gitlab_headers() @@ -294,7 +298,7 @@ class GitLabService(BaseGitService, GitService): try: tasks: list[SuggestedTask] = [] - + # Get merge requests using GraphQL response = await self.execute_graphql_query(query) data = response.get('currentUser', {}) @@ -343,27 +347,27 @@ class GitLabService(BaseGitService, GitService): title=title, ) ) - + # Get assigned issues using REST API - url = f"{self.BASE_URL}/issues" + url = f'{self.BASE_URL}/issues' params = { - "assignee_username": username, - "state": "opened", - "scope": "assigned_to_me" + 'assignee_username': username, + 'state': 'opened', + 'scope': 'assigned_to_me', } - + issues_response, _ = await self._make_request( - method=RequestMethod.GET, - url=url, - params=params + method=RequestMethod.GET, url=url, params=params ) - + # Process issues for issue in issues_response: - repo_name = issue.get('references', {}).get('full', '').split('#')[0].strip() + repo_name = ( + issue.get('references', {}).get('full', '').split('#')[0].strip() + ) issue_number = issue.get('iid') title = issue.get('title', '') - + tasks.append( SuggestedTask( git_provider=ProviderType.GITLAB, diff --git a/openhands/server/config/server_config.py b/openhands/server/config/server_config.py index 76ba29abc8..46427dcf35 100644 --- a/openhands/server/config/server_config.py +++ b/openhands/server/config/server_config.py @@ -20,7 +20,9 @@ class ServerConfig(ServerConfigInterface): ) conversation_manager_class: str = 'openhands.server.conversation_manager.standalone_conversation_manager.StandaloneConversationManager' monitoring_listener_class: str = 'openhands.server.monitoring.MonitoringListener' - user_auth_class: str = 'openhands.server.user_auth.default_user_auth.DefaultUserAuth' + user_auth_class: str = ( + 'openhands.server.user_auth.default_user_auth.DefaultUserAuth' + ) def verify_config(self): if self.config_cls: diff --git a/openhands/server/routes/files.py b/openhands/server/routes/files.py index adbf0e1fec..62f27b154a 100644 --- a/openhands/server/routes/files.py +++ b/openhands/server/routes/files.py @@ -234,7 +234,7 @@ async def git_diff( request: Request, path: str, conversation_id: str, - conversation_store = Depends(get_conversation_store), + conversation_store=Depends(get_conversation_store), ): runtime: Runtime = request.state.conversation.runtime diff --git a/openhands/server/routes/git.py b/openhands/server/routes/git.py index 82abe00a9e..f4fdc86d01 100644 --- a/openhands/server/routes/git.py +++ b/openhands/server/routes/git.py @@ -2,11 +2,9 @@ from fastapi import APIRouter, Depends, status from fastapi.responses import JSONResponse from pydantic import SecretStr -from openhands.integrations.github.github_service import GithubServiceImpl from openhands.integrations.provider import ( PROVIDER_TOKEN_TYPE, ProviderHandler, - ProviderType, ) from openhands.integrations.service_types import ( AuthenticationError, @@ -166,4 +164,4 @@ async def get_suggested_tasks( return JSONResponse( content='No providers set.', status_code=status.HTTP_401_UNAUTHORIZED, - ) \ No newline at end of file + ) diff --git a/openhands/server/routes/manage_conversations.py b/openhands/server/routes/manage_conversations.py index c178d9c482..896b988703 100644 --- a/openhands/server/routes/manage_conversations.py +++ b/openhands/server/routes/manage_conversations.py @@ -344,9 +344,7 @@ async def update_conversation( title: str = Body(embed=True), user_id: str | None = Depends(get_user_id), ) -> bool: - conversation_store = await ConversationStoreImpl.get_instance( - config, user_id - ) + conversation_store = await ConversationStoreImpl.get_instance(config, user_id) metadata = await conversation_store.get_metadata(conversation_id) if not metadata: return False @@ -369,9 +367,7 @@ async def delete_conversation( conversation_id: str, user_id: str | None = Depends(get_user_id), ) -> bool: - conversation_store = await ConversationStoreImpl.get_instance( - config, user_id - ) + conversation_store = await ConversationStoreImpl.get_instance(config, user_id) try: await conversation_store.get_metadata(conversation_id) except FileNotFoundError: diff --git a/openhands/server/routes/settings.py b/openhands/server/routes/settings.py index 45c2968bff..3a16e61b70 100644 --- a/openhands/server/routes/settings.py +++ b/openhands/server/routes/settings.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Request, status +from fastapi import APIRouter, Depends, status from fastapi.responses import JSONResponse from pydantic import SecretStr @@ -17,8 +17,7 @@ from openhands.server.settings import ( POSTSettingsModel, Settings, ) -from openhands.server.shared import SettingsStoreImpl, config, server_config -from openhands.server.types import AppMode +from openhands.server.shared import config from openhands.server.user_auth import ( get_provider_tokens, get_user_id, @@ -59,7 +58,8 @@ async def load_settings( settings_with_token_data = GETSettingsModel( **settings.model_dump(exclude='secrets_store'), - llm_api_key_set=settings.llm_api_key is not None and bool(settings.llm_api_key), + llm_api_key_set=settings.llm_api_key is not None + and bool(settings.llm_api_key), provider_tokens_set=provider_tokens_set, ) settings_with_token_data.llm_api_key = None @@ -211,9 +211,7 @@ async def reset_settings() -> JSONResponse: """ Resets user settings. (Deprecated) """ - logger.warning( - f"Deprecated endpoint /api/reset-settings called by user" - ) + logger.warning('Deprecated endpoint /api/reset-settings called by user') return JSONResponse( status_code=status.HTTP_410_GONE, content={'error': 'Reset settings functionality has been removed.'}, diff --git a/openhands/server/user_auth/user_auth.py b/openhands/server/user_auth/user_auth.py index e3f1738c72..a565d65d84 100644 --- a/openhands/server/user_auth/user_auth.py +++ b/openhands/server/user_auth/user_auth.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os from abc import ABC, abstractmethod from fastapi import Request