mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-11 13:58:58 +00:00
feat(core): Add code editor for UI component
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import hashlib
|
import hashlib
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
@@ -24,6 +25,7 @@ from .storage import (
|
|||||||
StorageItem,
|
StorageItem,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
_SCHEMA = "dbgpt-fs"
|
_SCHEMA = "dbgpt-fs"
|
||||||
|
|
||||||
|
|
||||||
@@ -133,6 +135,14 @@ class FileStorageURI:
|
|||||||
self.version = version
|
self.version = version
|
||||||
self.custom_params = custom_params or {}
|
self.custom_params = custom_params or {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_local_file(cls, uri: str) -> bool:
|
||||||
|
"""Check if the URI is local."""
|
||||||
|
parsed = urlparse(uri)
|
||||||
|
if not parsed.scheme or parsed.scheme == "file":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, uri: str) -> "FileStorageURI":
|
def parse(cls, uri: str) -> "FileStorageURI":
|
||||||
"""Parse the URI string."""
|
"""Parse the URI string."""
|
||||||
@@ -313,6 +323,13 @@ class FileStorageSystem:
|
|||||||
file_size = file_data.tell() # Get the file size
|
file_size = file_data.tell() # Get the file size
|
||||||
file_data.seek(0) # Reset file pointer
|
file_data.seek(0) # Reset file pointer
|
||||||
|
|
||||||
|
# filter None value
|
||||||
|
custom_metadata = (
|
||||||
|
{k: v for k, v in custom_metadata.items() if v is not None}
|
||||||
|
if custom_metadata
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
|
||||||
with root_tracer.start_span(
|
with root_tracer.start_span(
|
||||||
"file_storage_system.save_file.calculate_hash",
|
"file_storage_system.save_file.calculate_hash",
|
||||||
):
|
):
|
||||||
@@ -329,7 +346,7 @@ class FileStorageSystem:
|
|||||||
storage_type=storage_type,
|
storage_type=storage_type,
|
||||||
storage_path=storage_path,
|
storage_path=storage_path,
|
||||||
uri=str(uri),
|
uri=str(uri),
|
||||||
custom_metadata=custom_metadata or {},
|
custom_metadata=custom_metadata,
|
||||||
file_hash=file_hash,
|
file_hash=file_hash,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -339,6 +356,25 @@ class FileStorageSystem:
|
|||||||
@trace("file_storage_system.get_file")
|
@trace("file_storage_system.get_file")
|
||||||
def get_file(self, uri: str) -> Tuple[BinaryIO, FileMetadata]:
|
def get_file(self, uri: str) -> Tuple[BinaryIO, FileMetadata]:
|
||||||
"""Get the file data from the storage backend."""
|
"""Get the file data from the storage backend."""
|
||||||
|
if FileStorageURI.is_local_file(uri):
|
||||||
|
local_file_name = uri.split("/")[-1]
|
||||||
|
if not os.path.exists(uri):
|
||||||
|
raise FileNotFoundError(f"File not found: {uri}")
|
||||||
|
|
||||||
|
dummy_metadata = FileMetadata(
|
||||||
|
file_id=local_file_name,
|
||||||
|
bucket="dummy_bucket",
|
||||||
|
file_name=local_file_name,
|
||||||
|
file_size=-1,
|
||||||
|
storage_type="local",
|
||||||
|
storage_path=uri,
|
||||||
|
uri=uri,
|
||||||
|
custom_metadata={},
|
||||||
|
file_hash="",
|
||||||
|
)
|
||||||
|
logger.info(f"Reading local file: {uri}")
|
||||||
|
return open(uri, "rb"), dummy_metadata # noqa: SIM115
|
||||||
|
|
||||||
parsed_uri = FileStorageURI.parse(uri)
|
parsed_uri = FileStorageURI.parse(uri)
|
||||||
metadata = self.metadata_storage.load(
|
metadata = self.metadata_storage.load(
|
||||||
FileMetadataIdentifier(
|
FileMetadataIdentifier(
|
||||||
|
@@ -118,7 +118,9 @@ async def test_auth():
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/flows", response_model=Result[None], dependencies=[Depends(check_api_key)]
|
"/flows",
|
||||||
|
response_model=Result[ServerResponse],
|
||||||
|
dependencies=[Depends(check_api_key)],
|
||||||
)
|
)
|
||||||
async def create(
|
async def create(
|
||||||
request: ServeRequest, service: Service = Depends(get_service)
|
request: ServeRequest, service: Service = Depends(get_service)
|
||||||
|
@@ -1079,3 +1079,132 @@ class ExampleFlowTagsOperator(MapOperator[str, str]):
|
|||||||
async def map(self, user_name: str) -> str:
|
async def map(self, user_name: str) -> str:
|
||||||
"""Map the user name to the tags."""
|
"""Map the user name to the tags."""
|
||||||
return "Your name is %s, and your tags are %s." % (user_name, "higher-order")
|
return "Your name is %s, and your tags are %s." % (user_name, "higher-order")
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleFlowCodeEditorOperator(MapOperator[str, str]):
|
||||||
|
"""An example flow operator that includes a code editor as parameter."""
|
||||||
|
|
||||||
|
metadata = ViewMetadata(
|
||||||
|
label="Example Flow Code Editor",
|
||||||
|
name="example_flow_code_editor",
|
||||||
|
category=OperatorCategory.EXAMPLE,
|
||||||
|
description="An example flow operator that includes a code editor as parameter.",
|
||||||
|
parameters=[
|
||||||
|
Parameter.build_from(
|
||||||
|
"Code Editor",
|
||||||
|
"code",
|
||||||
|
type=str,
|
||||||
|
placeholder="Please input your code",
|
||||||
|
description="The code you want to edit.",
|
||||||
|
ui=ui.UICodeEditor(
|
||||||
|
language="python",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
inputs=[
|
||||||
|
IOField.build_from(
|
||||||
|
"User Name",
|
||||||
|
"user_name",
|
||||||
|
str,
|
||||||
|
description="The name of the user.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IOField.build_from(
|
||||||
|
"Code",
|
||||||
|
"code",
|
||||||
|
str,
|
||||||
|
description="Result of the code.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, code: str, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
async def map(self, user_name: str) -> str:
|
||||||
|
"""Map the user name to the code."""
|
||||||
|
from dbgpt.util.code_utils import UNKNOWN, extract_code
|
||||||
|
|
||||||
|
code = self.code
|
||||||
|
exitcode = -1
|
||||||
|
try:
|
||||||
|
code_blocks = extract_code(self.code)
|
||||||
|
if len(code_blocks) < 1:
|
||||||
|
logger.info(
|
||||||
|
f"No executable code found in: \n{code}",
|
||||||
|
)
|
||||||
|
raise ValueError(f"No executable code found in: \n{code}")
|
||||||
|
elif len(code_blocks) > 1 and code_blocks[0][0] == UNKNOWN:
|
||||||
|
# found code blocks, execute code and push "last_n_messages" back
|
||||||
|
logger.info(
|
||||||
|
f"Missing available code block type, unable to execute code,"
|
||||||
|
f"\n{code}",
|
||||||
|
)
|
||||||
|
raise ValueError(
|
||||||
|
"Missing available code block type, unable to execute code, "
|
||||||
|
f"\n{code}"
|
||||||
|
)
|
||||||
|
exitcode, logs = await self.blocking_func_to_async(
|
||||||
|
self.execute_code_blocks, code_blocks
|
||||||
|
)
|
||||||
|
# exitcode, logs = self.execute_code_blocks(code_blocks)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to execute code: {e}")
|
||||||
|
logs = f"Failed to execute code: {e}"
|
||||||
|
return (
|
||||||
|
f"Your name is {user_name}, and your code is \n\n```python\n{self.code}"
|
||||||
|
f"\n\n```\n\nThe execution result is \n\n```\n{logs}\n\n```\n\n"
|
||||||
|
f"Exit code: {exitcode}."
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute_code_blocks(self, code_blocks):
|
||||||
|
"""Execute the code blocks and return the result."""
|
||||||
|
from dbgpt.util.code_utils import execute_code, infer_lang
|
||||||
|
from dbgpt.util.utils import colored
|
||||||
|
|
||||||
|
logs_all = ""
|
||||||
|
exitcode = -1
|
||||||
|
_code_execution_config = {"use_docker": False}
|
||||||
|
for i, code_block in enumerate(code_blocks):
|
||||||
|
lang, code = code_block
|
||||||
|
if not lang:
|
||||||
|
lang = infer_lang(code)
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"\n>>>>>>>> EXECUTING CODE BLOCK {i} "
|
||||||
|
f"(inferred language is {lang})...",
|
||||||
|
"red",
|
||||||
|
),
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
if lang in ["bash", "shell", "sh"]:
|
||||||
|
exitcode, logs, image = execute_code(
|
||||||
|
code, lang=lang, **_code_execution_config
|
||||||
|
)
|
||||||
|
elif lang in ["python", "Python"]:
|
||||||
|
if code.startswith("# filename: "):
|
||||||
|
filename = code[11 : code.find("\n")].strip()
|
||||||
|
else:
|
||||||
|
filename = None
|
||||||
|
exitcode, logs, image = execute_code(
|
||||||
|
code,
|
||||||
|
lang="python",
|
||||||
|
filename=filename,
|
||||||
|
**_code_execution_config,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# In case the language is not supported, we return an error message.
|
||||||
|
exitcode, logs, image = (
|
||||||
|
1,
|
||||||
|
f"unknown language {lang}",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
# raise NotImplementedError
|
||||||
|
if image is not None:
|
||||||
|
_code_execution_config["use_docker"] = image
|
||||||
|
logs_all += "\n" + logs
|
||||||
|
if exitcode != 0:
|
||||||
|
return exitcode, logs_all
|
||||||
|
return exitcode, logs_all
|
||||||
|
Reference in New Issue
Block a user