feat(core): Add code editor for UI component

This commit is contained in:
Fangyin Cheng
2024-08-19 15:39:42 +08:00
parent 85369a55a7
commit 1c7a6c9122
3 changed files with 169 additions and 2 deletions

View File

@@ -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(

View File

@@ -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)

View File

@@ -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