From 40f838db09fe26f24a444ae876fa2c6453a36d65 Mon Sep 17 00:00:00 2001 From: Liqun Li Date: Wed, 9 Oct 2024 16:03:49 +0800 Subject: [PATCH 1/2] fix doc mismatching (#422) Co-authored-by: Jack-Q --- auto_eval/ds1000_scripts/README.md | 4 +- taskweaver/llm/openai.py | 5 --- website/blog/experience.md | 28 +++++++++----- website/docs/configurations/overview.md | 15 +++++--- website/docs/llms/aoai.md | 50 +++++++++++++------------ 5 files changed, 57 insertions(+), 45 deletions(-) diff --git a/auto_eval/ds1000_scripts/README.md b/auto_eval/ds1000_scripts/README.md index 7c984d93..f59d97a2 100644 --- a/auto_eval/ds1000_scripts/README.md +++ b/auto_eval/ds1000_scripts/README.md @@ -15,8 +15,8 @@ This directory contains the scripts used to evaluate the performance of the [DS- - metadata.json: the metadata of the test case. - prompt.txt: the composed prompt of the test case. - reference_code.py: the ground truth code. -4. Copy the example files from `ds1000_scritps/planner_examples` to `project/planner_examples` directory; - and the example files from `ds1000_scritps/codeinterpreter_examples` to `project/codeinterpreter_examples` directory. +4. Copy the example files from `ds1000_scritps/planner_examples` to `project/examples/planner_examples` directory; + and the example files from `ds1000_scritps/codeinterpreter_examples` to `project/examples/code_generator_examples` directory. Disable (or discard) the original example files from the project directory. See the notes below for understanding why. 5. Once the test cases are generated, follow the instructions in `auto_eval/README.md` to evaluate the performance of the benchmark. diff --git a/taskweaver/llm/openai.py b/taskweaver/llm/openai.py index d4752219..0f386937 100644 --- a/taskweaver/llm/openai.py +++ b/taskweaver/llm/openai.py @@ -51,11 +51,6 @@ def _configure(self) -> None: # openai specific config self.api_version = self._get_str("api_version", "2024-06-01") - self.api_auth_type = self._get_enum( - "api_auth_type", - ["openai", "azure", "azure_ad"], - "openai", - ) is_azure_ad_login = self.api_type == "azure_ad" self.aad_auth_mode = self._get_enum( "aad_auth_mode", diff --git a/website/blog/experience.md b/website/blog/experience.md index 831f281a..5c36e2b5 100644 --- a/website/blog/experience.md +++ b/website/blog/experience.md @@ -98,15 +98,25 @@ def reply(self, memory: Memory, **kwargs: ...) -> Post: In a role that needs to set the experience subdirectory, we can get the experience subdirectory from the shared memory. ```python -exp_sub_paths = memory.get_shared_memory_entries( - entry_type="experience_sub_path", -) - -if exp_sub_paths: - exp_sub_path = exp_sub_paths[0].content -else: - exp_sub_path = "" -selected_experiences = self.role_load_experience(query=query, sub_path=exp_sub_path) +def reply( + self, + memory: Memory, + post_proxy: Optional[PostEventProxy] = None, + prompt_log_path: Optional[str] = None, + **kwargs: ..., + ) -> Post: + ... + rounds = memory.get_role_rounds( + role=self.alias, + include_failure_rounds=False, + ) + + # obtain the query from the last round + query = rounds[-1].post_list[-1].message + + # retrieve the experience based on the query + self.role_load_experience(query=query, memory=memory) + ... ``` :::tip diff --git a/website/docs/configurations/overview.md b/website/docs/configurations/overview.md index 6de552df..aab9d111 100644 --- a/website/docs/configurations/overview.md +++ b/website/docs/configurations/overview.md @@ -24,22 +24,27 @@ The following table lists the parameters in the configuration file: | `logging.log_file` | The name of the log file. | `taskweaver.log` | | `logging.log_folder` | The folder to store the log file. | `logs` | | `plugin.base_path` | The folder to store plugins. | `${AppBaseDir}/plugins` | -| `planner.example_base_path` | The folder to store planner examples. | `${AppBaseDir}/planner_examples` | +| `{RoleName}.use_example` | Whether to use the example for the role. | `true` | +| `{RoleName}.example_base_path` | The folder to store the examples for the role. | `${AppBaseDir}/examples/{RoleName}_examples` | +| `{RoleName}.dynamic_example_sub_path` | Whether to enable dynamic example loading based on sub-path. | `false` | +| `{RoleName}.use_experience` | Whether to use experience summarized from the previous chat history for the role. | `false` | +| `{RoleName}.experience_dir` | The folder to store the experience for the role. | `${AppBaseDir}/experience/` | +| `{RoleName}.dynamic_experience_sub_path` | Whether to enable dynamic experience loading based on sub-path. | `false` | | `planner.prompt_compression` | Whether to compress the chat history for planner. | `false` | -| `planner.use_experience` | Whether to use experience summarized from the previous chat history in planner. | `false` | -| `code_generator.example_base_path` | The folder to store code interpreter examples. | `${AppBaseDir}/codeinterpreter_examples` | | `code_generator.prompt_compression` | Whether to compress the chat history for code interpreter. | `false` | | `code_generator.enable_auto_plugin_selection` | Whether to enable auto plugin selection. | `false` | -| `code_generator.use_experience` | Whether to use experience summarized from the previous chat history in code generator. | `false` | | `code_generator.auto_plugin_selection_topk` | The number of auto selected plugins in each round. | `3` | | `session.max_internal_chat_round_num` | The maximum number of internal chat rounds between Planner and Code Interpreter. | `10` | | `session.roles` | The roles included for the conversation. | ["planner", "code_interpreter"] | | `round_compressor.rounds_to_compress` | The number of rounds to compress. | `2` | | `round_compressor.rounds_to_retain` | The number of rounds to retain. | `3` | -| `execution_service.kernel_mode` | The mode of the code executor, could be `local` or `container`. | `local` | +| `execution_service.kernel_mode` | The mode of the code executor, could be `local` or `container`. | `container` | :::tip $\{AppBaseDir\} is the project directory. + +$\{RoleName\} is the name of the role, such as `planner` or `code_generator`. In the current implementation, the `code_interpreter` role has all code generation functions +in a "sub-role" named `code_generator`. So, the configuration for the code generation part should be set to `code_generator`. ::: :::tip diff --git a/website/docs/llms/aoai.md b/website/docs/llms/aoai.md index a98f8107..79762cde 100644 --- a/website/docs/llms/aoai.md +++ b/website/docs/llms/aoai.md @@ -8,24 +8,25 @@ description: Using LLMs from OpenAI/AOAI 1. Create an account on [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) and get your API key. 2. Create a new deployment of the model and get the deployment name. 3. Add the following to your `taskweaver_config.json` file: -```jsonc showLineNumbers -{ - "llm.api_base":"YOUR_AOAI_ENDPOINT", // in the format of https://.openai.azure.com" - "llm.api_key":"YOUR_API_KEY", - "llm.api_type":"azure", - "llm.auth_mode":"api-key", - "llm.model":"gpt-4-1106-preview", // this is known as deployment_name in Azure OpenAI - "llm.response_format": "json_object" -} -``` + ```jsonc showLineNumbers + { + "llm.api_base":"YOUR_AOAI_ENDPOINT", // in the format of https://.openai.azure.com" + "llm.api_key":"YOUR_API_KEY", + "llm.api_type":"azure", + "llm.model":"gpt-4-1106-preview", // this is known as deployment_name in Azure OpenAI + "llm.response_format": "json_object", + "llm.azure.api_version": "2024-06-01" + } + ``` -:::info -For model versions or after `1106`, `llm.response_format` can be set to `json_object`. -However, for the earlier models, which do not support JSON response explicitly, `llm.response_format` should be set to `null`. -::: + :::info + For model versions or after `1106`, `llm.response_format` can be set to `json_object`. + However, for the earlier models, which do not support JSON response explicitly, `llm.response_format` should be set to `null`. + ::: 4. Start TaskWeaver and chat with TaskWeaver. -You can refer to the [Quick Start](../quickstart.md) for more details. + + You can refer to the [Quick Start](../quickstart.md) for more details. ## Using Entra Authentication @@ -33,15 +34,16 @@ You can refer to the [Quick Start](../quickstart.md) for more details. [assign the proper Azure RBAC Role](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/role-based-access-control) to your account (or service principal). 2. Create a new deployment of the model and get the deployment name. 3. Add the following to your `taskweaver_config.json` file: - ```jsonc showLineNumbers - { - "llm.api_base":"YOUR_AOAI_ENDPOINT", // in the format of https://.openai.azure.com" - "llm.api_type":"azure_ad", - "llm.auth_mode":"default_azure_credential", - "llm.model":"gpt-4-1106-preview", // this is known as deployment_name in Azure OpenAI - "llm.response_format": "json_object" - } - ``` + ```jsonc showLineNumbers + { + "llm.api_base":"YOUR_AOAI_ENDPOINT", // in the format of https://.openai.azure.com" + "llm.api_type":"azure_ad", + "llm.model":"gpt-4-1106-preview", // this is known as deployment_name in Azure OpenAI + "llm.response_format": "json_object", + "llm.azure_ad.api_version": "2024-06-01", + "llm.azure_ad.aad_auth_mode": "default_azure_credential" + } + ``` 4. Install extra dependencies: ```bash pip install azure-identity From 683501b3d179043164c05bd89647d3de4bd2af4b Mon Sep 17 00:00:00 2001 From: Liqun Li Date: Thu, 10 Oct 2024 15:18:47 +0800 Subject: [PATCH 2/2] Fix container related issues (#426) 1. support custom image 2. fix the issue of import failure --- .gitattributes | 1 + docker/ces_container/Dockerfile | 2 +- scripts/build_executor.ps1 | 2 +- taskweaver/ces/__init__.py | 4 ++- taskweaver/ces/environment.py | 35 +++++++++++++++++--------- taskweaver/ces/kernel/launcher.py | 8 +++++- taskweaver/ces/manager/sub_proc.py | 2 ++ taskweaver/module/execution_service.py | 7 ++++++ 8 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfdb8b77 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/docker/ces_container/Dockerfile b/docker/ces_container/Dockerfile index 768f3686..1d17c2fe 100644 --- a/docker/ces_container/Dockerfile +++ b/docker/ces_container/Dockerfile @@ -17,7 +17,7 @@ COPY taskweaver/__init__.py /app/taskweaver/__init__.py COPY docker/ces_container/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh -ENV PYTHONPATH "${PYTHONPATH}:/app" +ENV PYTHONPATH="/app" ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/scripts/build_executor.ps1 b/scripts/build_executor.ps1 index 68631fa5..5ef6096c 100644 --- a/scripts/build_executor.ps1 +++ b/scripts/build_executor.ps1 @@ -1,7 +1,7 @@ $scriptDirectory = $PSScriptRoot Write-Host "The script directory is: $scriptDirectory" -$version = "0.2" +$version = "0.3" $imageName = "taskweavercontainers/taskweaver-executor" $imageFullName = "${imageName}:${version}" diff --git a/taskweaver/ces/__init__.py b/taskweaver/ces/__init__.py index 6ecc8ee8..7d8a45eb 100644 --- a/taskweaver/ces/__init__.py +++ b/taskweaver/ces/__init__.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Literal, Optional from taskweaver.ces.common import Manager from taskweaver.ces.manager.defer import DeferredManager @@ -8,11 +8,13 @@ def code_execution_service_factory( env_dir: str, kernel_mode: Literal["local", "container"] = "local", + custom_image: Optional[str] = None, ) -> Manager: def sub_proc_manager_factory() -> SubProcessManager: return SubProcessManager( env_dir=env_dir, kernel_mode=kernel_mode, + custom_image=custom_image, ) return DeferredManager( diff --git a/taskweaver/ces/environment.py b/taskweaver/ces/environment.py index ae7c21d2..84000204 100644 --- a/taskweaver/ces/environment.py +++ b/taskweaver/ces/environment.py @@ -111,12 +111,15 @@ class EnvMode(enum.Enum): class Environment: + DEFAULT_IMAGE = "taskweavercontainers/taskweaver-executor:latest" + def __init__( self, env_id: Optional[str] = None, env_dir: Optional[str] = None, env_mode: Optional[EnvMode] = EnvMode.Local, port_start_inside_container: Optional[int] = 12345, + custom_image: Optional[str] = None, ) -> None: self.session_dict: Dict[str, EnvSession] = {} self.id = get_id(prefix="env") if env_id is None else env_id @@ -145,19 +148,27 @@ def __init__( except docker.errors.DockerException as e: raise docker.errors.DockerException(f"Failed to connect to Docker daemon: {e}. ") - self.image_name = "taskweavercontainers/taskweaver-executor:latest" - try: - local_image = self.docker_client.images.get(self.image_name) - registry_image = self.docker_client.images.get_registry_data(self.image_name) - if local_image.id != registry_image.id: - logger.info(f"Local image {local_image.id} does not match registry image {registry_image.id}.") - raise docker.errors.ImageNotFound("Local image is outdated.") - except docker.errors.ImageNotFound: - logger.info("Pulling image from docker.io.") + if custom_image: + logger.info(f"Using custom image {custom_image}.") + self.image_name = custom_image + try: + self.docker_client.images.get(self.image_name) + except docker.errors.ImageNotFound: + raise docker.errors.ImageNotFound(f"Custom image {self.image_name} not found.") + else: + self.image_name = self.DEFAULT_IMAGE try: - self.docker_client.images.pull(self.image_name) - except docker.errors.DockerException as e: - raise docker.errors.DockerException(f"Failed to pull image: {e}. ") + local_image = self.docker_client.images.get(self.image_name) + registry_image = self.docker_client.images.get_registry_data(self.image_name) + if local_image.id != registry_image.id: + logger.info(f"Local image {local_image.id} does not match registry image {registry_image.id}.") + raise docker.errors.ImageNotFound("Local image is outdated.") + except docker.errors.ImageNotFound: + logger.info("Pulling image from docker.io.") + try: + self.docker_client.images.pull(self.image_name) + except docker.errors.DockerException as e: + raise docker.errors.DockerException(f"Failed to pull image: {e}. ") self.session_container_dict: Dict[str, str] = {} self.port_start_inside_container = port_start_inside_container diff --git a/taskweaver/ces/kernel/launcher.py b/taskweaver/ces/kernel/launcher.py index 6d516a8d..759129a9 100644 --- a/taskweaver/ces/kernel/launcher.py +++ b/taskweaver/ces/kernel/launcher.py @@ -1,7 +1,6 @@ import os import sys -from taskweaver.ces.kernel.ext import TaskWeaverZMQShellDisplayHook from taskweaver.ces.kernel.kernel_logging import logger kernel_mode = os.getenv("TASKWEAVER_KERNEL_MODE", "local") @@ -56,6 +55,8 @@ def start_app(): from ipykernel.kernelapp import IPKernelApp from ipykernel.zmqshell import ZMQInteractiveShell + from taskweaver.ces.kernel.ext import TaskWeaverZMQShellDisplayHook + # override displayhook_class for skipping output suppress token issue ZMQInteractiveShell.displayhook_class = TaskWeaverZMQShellDisplayHook @@ -82,6 +83,11 @@ def start_app(): if __name__ == "__main__": if sys.path[0] == "": del sys.path[0] + import site + + user_site_packages = site.getusersitepackages() + if user_site_packages not in sys.path: + sys.path.append(site.getusersitepackages()) logger.info("Starting process...") logger.info("sys.path: %s", sys.path) logger.info("os.getcwd(): %s", os.getcwd()) diff --git a/taskweaver/ces/manager/sub_proc.py b/taskweaver/ces/manager/sub_proc.py index aee81c28..e7fa450d 100644 --- a/taskweaver/ces/manager/sub_proc.py +++ b/taskweaver/ces/manager/sub_proc.py @@ -57,6 +57,7 @@ def __init__( env_id: Optional[str] = None, env_dir: Optional[str] = None, kernel_mode: KernelModeType = "local", + custom_image: Optional[str] = None, ) -> None: from taskweaver.ces.environment import Environment, EnvMode @@ -76,6 +77,7 @@ def __init__( env_id, env_dir, env_mode=env_mode, + custom_image=custom_image, ) def initialize(self) -> None: diff --git a/taskweaver/module/execution_service.py b/taskweaver/module/execution_service.py index 0640ae39..ac46ac67 100644 --- a/taskweaver/module/execution_service.py +++ b/taskweaver/module/execution_service.py @@ -19,6 +19,7 @@ def _configure(self) -> None: "kernel_mode", "container", ) + assert self.kernel_mode in ["local", "container"], f"Invalid kernel mode: {self.kernel_mode}" if self.kernel_mode == "local": print( "TaskWeaver is running in the `local` mode. This implies that " @@ -27,6 +28,11 @@ def _configure(self) -> None: "More information can be found in the documentation " "(https://microsoft.github.io/TaskWeaver/docs/code_execution/).", ) + self.custom_image = self._get_str( + "custom_image", + default=None, + required=False, + ) class ExecutionServiceModule(Module): @@ -39,5 +45,6 @@ def provide_executor_manager(self, config: ExecutionServiceConfig) -> Manager: self.manager = code_execution_service_factory( env_dir=config.env_dir, kernel_mode=config.kernel_mode, + custom_image=config.custom_image, ) return self.manager