|
|
@@ -1,12 +1,13 @@
|
|
|
+import json
|
|
|
import uuid
|
|
|
from pathlib import Path
|
|
|
|
|
|
import litellm
|
|
|
-from fastapi import Depends, FastAPI, WebSocket, HTTPException, Query, status
|
|
|
+from fastapi import Depends, FastAPI, HTTPException, Query, Response, WebSocket, status
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
+from fastapi.responses import JSONResponse, RedirectResponse
|
|
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
-from fastapi.responses import RedirectResponse, JSONResponse
|
|
|
|
|
|
import agenthub # noqa F401 (we import this to get the agents registered)
|
|
|
from opendevin import config, files
|
|
|
@@ -45,9 +46,9 @@ async def websocket_endpoint(websocket: WebSocket):
|
|
|
|
|
|
@app.get('/api/litellm-models')
|
|
|
async def get_litellm_models():
|
|
|
- """
|
|
|
+ '''
|
|
|
Get all models supported by LiteLLM.
|
|
|
- """
|
|
|
+ '''
|
|
|
return list(set(litellm.model_list + list(litellm.model_cost.keys())))
|
|
|
|
|
|
|
|
|
@@ -72,7 +73,9 @@ async def get_token(
|
|
|
sid = get_sid_from_token(credentials.credentials)
|
|
|
if not sid:
|
|
|
sid = str(uuid.uuid4())
|
|
|
- logger.info(f'Invalid or missing credentials, generating new session ID: {sid}')
|
|
|
+ logger.info(
|
|
|
+ f'Invalid or missing credentials, generating new session ID: {sid}'
|
|
|
+ )
|
|
|
else:
|
|
|
sid = str(uuid.uuid4())
|
|
|
logger.info(f'No credentials provided, generating new session ID: {sid}')
|
|
|
@@ -117,7 +120,9 @@ def refresh_files():
|
|
|
|
|
|
|
|
|
@app.get('/api/list-files')
|
|
|
-def list_files(relpath: str = Query(None, description='Relative path from workspace base')):
|
|
|
+def list_files(
|
|
|
+ relpath: str = Query(None, description='Relative path from workspace base')
|
|
|
+):
|
|
|
"""Refreshes and returns the files and directories from a specified subdirectory or the base directory if no subdirectory is specified, limited to one level deep."""
|
|
|
base_path = Path(config.get('WORKSPACE_BASE')).resolve()
|
|
|
full_path = (base_path / relpath).resolve() if relpath is not None else base_path
|
|
|
@@ -127,7 +132,11 @@ def list_files(relpath: str = Query(None, description='Relative path from worksp
|
|
|
# Ensure path exists, is a directory,
|
|
|
# And is within the workspace base directory - to prevent directory traversal attacks
|
|
|
# https://owasp.org/www-community/attacks/Path_Traversal
|
|
|
- if not full_path.exists() or not full_path.is_dir() or not str(full_path).startswith(str(base_path)):
|
|
|
+ if (
|
|
|
+ not full_path.exists()
|
|
|
+ or not full_path.is_dir()
|
|
|
+ or not str(full_path).startswith(str(base_path))
|
|
|
+ ):
|
|
|
raise HTTPException(status_code=400, detail='Invalid path provided.')
|
|
|
|
|
|
structure = files.get_single_level_folder_structure(base_path, full_path)
|
|
|
@@ -153,6 +162,28 @@ def select_file(file: str):
|
|
|
return {'code': content}
|
|
|
|
|
|
|
|
|
+@app.get('/api/plan')
|
|
|
+def get_plan(
|
|
|
+ credentials: HTTPAuthorizationCredentials = Depends(security_scheme),
|
|
|
+):
|
|
|
+ sid = get_sid_from_token(credentials.credentials)
|
|
|
+ agent = agent_manager.sid_to_agent[sid]
|
|
|
+ controller = agent.controller
|
|
|
+ if controller is not None:
|
|
|
+ state = controller.get_state()
|
|
|
+ if state is not None:
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=status.HTTP_200_OK,
|
|
|
+ content=json.dumps(
|
|
|
+ {
|
|
|
+ 'mainGoal': state.plan.main_goal,
|
|
|
+ 'task': state.plan.task.to_dict(),
|
|
|
+ }
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
|
+
|
|
|
+
|
|
|
@app.get('/')
|
|
|
async def docs_redirect():
|
|
|
response = RedirectResponse(url='/index.html')
|