Compare commits

...

19 Commits

Author SHA1 Message Date
Yuxiang Dong(Jerry) af65ad8cbf
Merge 139d28bfd6 into 76a4bd05d9 2024-10-17 10:11:56 -07:00
Daniel Chalef 76a4bd05d9
Add Zep ecosystem doc and notebook (#3681)
* Add Zep ecosystem doc and notebook

* fix linting and formatting issues

* Fix url

* Update agent-memory-with-zep.md

* add notebook metadata

* newline

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2024-10-17 10:08:30 -07:00
Lokesh Goel 1960eaba1a
Add Couchbase Vector DB Example Notebook and Minor Bug Fix (#3804) 2024-10-17 14:18:03 +02:00
kiyoung 02977ee250
K8s code executor (#3419)
* remove coding directory from gitignore

* add k8s PodCommandLineCodeExecutor

* add error handlings and custom pod spec parameter

* change parameter name to kube_config_file

* add param container_name

* add test case for PodCommandLineCodeExecutor

* add test guide

* draft for docs notebook

* test code fix indent

* add document

* add license info

* revise documentation

* modify document: install autogen-agentchat

* apply pre-commit

* revert change to gitignore

* error handling: move import block into try block

---------

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2024-10-16 12:22:50 -07:00
Victor Dibia 7e297149f8
align tagline buttons (#3788) 2024-10-14 19:00:53 -04:00
DONG, JERRY 139d28bfd6 added changes per github comments and run precommit 2024-10-04 10:50:41 -05:00
Miguel Armenta 49a392ab35
Merge branch 'main' into post_process_message_redaction_sample 2024-07-03 11:04:47 -05:00
Yuxiang Dong(Jerry) a63db7cda4
Added changes based on comments in pr 2024-06-14 10:51:55 -05:00
Yuxiang Dong(Jerry) 4b8ab223c9
Merge branch 'microsoft:main' into post_process_message_redaction_sample 2024-06-12 11:10:49 -05:00
Yuxiang Dong(Jerry) 5fbb833a87
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 11:10:14 -05:00
Yuxiang Dong(Jerry) 6e6a276e6e
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 11:04:50 -05:00
Yuxiang Dong(Jerry) cd70b6d518
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 10:49:45 -05:00
Yuxiang Dong(Jerry) 229154a8e1
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 10:48:34 -05:00
Yuxiang Dong(Jerry) 53c04e2938
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 10:48:13 -05:00
Yuxiang Dong(Jerry) 15224f161e
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 10:46:07 -05:00
Yuxiang Dong(Jerry) 0a28a5082f
Update agentchat_postresponse_secret_redaction.ipynb 2024-06-12 10:45:20 -05:00
Yuxiang Dong(Jerry) 99337b38da
Removed cell output 2024-06-12 10:43:12 -05:00
Yuxiang Dong(Jerry) a319a65627
Update agentchat_postresponse_secret_redaction.ipynb, removed error from local 2024-06-12 10:36:02 -05:00
Yuxiang Dong(Jerry) 8bca41e85a
Add files via upload
Added a python notebook to show an example of how to redact sensitive data after an agent generates a response
2024-06-12 10:23:42 -05:00
19 changed files with 3139 additions and 57 deletions

201
LICENSE-CODE-KUBERNETES Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -56,16 +56,7 @@ class CouchbaseVectorDB(VectorDB):
wait_until_index_ready (float | None): Blocking call to wait until the database indexes are ready. None means no wait. Default is None.
wait_until_document_ready (float | None): Blocking call to wait until the database documents are ready. None means no wait. Default is None.
"""
print(
"CouchbaseVectorDB",
connection_string,
username,
password,
bucket_name,
scope_name,
collection_name,
index_name,
)
self.embedding_function = embedding_function
self.index_name = index_name
@ -119,6 +110,7 @@ class CouchbaseVectorDB(VectorDB):
try:
collection_mgr = self.bucket.collections()
collection_mgr.create_collection(self.scope.name, collection_name)
self.cluster.query(f"CREATE PRIMARY INDEX ON {self.bucket.name}.{self.scope.name}.{collection_name}")
except Exception:
if not get_or_create:
@ -287,7 +279,12 @@ class CouchbaseVectorDB(VectorDB):
[doc["content"]]
).tolist() # Gets new embedding even in case of document update
doc_content = {TEXT_KEY: doc["content"], "metadata": doc.get("metadata", {}), EMBEDDING_KEY: embedding}
doc_content = {
TEXT_KEY: doc["content"],
"metadata": doc.get("metadata", {}),
EMBEDDING_KEY: embedding,
"id": doc_id,
}
docs_to_upsert[doc_id] = doc_content
collection.upsert_multi(docs_to_upsert)

View File

@ -0,0 +1,5 @@
from .pod_commandline_code_executor import PodCommandLineCodeExecutor
__all__ = [
"PodCommandLineCodeExecutor",
]

View File

@ -0,0 +1,323 @@
from __future__ import annotations
import atexit
import importlib
import sys
import textwrap
import uuid
from hashlib import md5
from pathlib import Path
from time import sleep
from types import TracebackType
from typing import Any, ClassVar, Dict, List, Optional, Type, Union
client = importlib.import_module("kubernetes.client")
config = importlib.import_module("kubernetes.config")
ApiException = importlib.import_module("kubernetes.client.rest").ApiException
stream = importlib.import_module("kubernetes.stream").stream
from ...code_utils import TIMEOUT_MSG, _cmd
from ..base import CodeBlock, CodeExecutor, CodeExtractor, CommandLineCodeResult
from ..markdown_code_extractor import MarkdownCodeExtractor
from ..utils import _get_file_name_from_content, silence_pip
if sys.version_info >= (3, 11):
from typing import Self
else:
from typing_extensions import Self
class PodCommandLineCodeExecutor(CodeExecutor):
DEFAULT_EXECUTION_POLICY: ClassVar[Dict[str, bool]] = {
"bash": True,
"shell": True,
"sh": True,
"pwsh": False,
"powershell": False,
"ps1": False,
"python": True,
"javascript": False,
"html": False,
"css": False,
}
LANGUAGE_ALIASES: ClassVar[Dict[str, str]] = {
"py": "python",
"js": "javascript",
}
LANGUAGE_FILE_EXTENSION: ClassVar[Dict[str, str]] = {
"python": "py",
"javascript": "js",
"bash": "sh",
"shell": "sh",
"sh": "sh",
}
def __init__(
self,
image: str = "python:3-slim",
pod_name: Optional[str] = None,
namespace: Optional[str] = None,
pod_spec: Optional[client.V1Pod] = None, # type: ignore
container_name: Optional[str] = "autogen-code-exec",
timeout: int = 60,
work_dir: Union[Path, str] = Path("/workspace"),
kube_config_file: Optional[str] = None,
stop_container: bool = True,
execution_policies: Optional[Dict[str, bool]] = None,
):
"""(Experimental) A code executor class that executes code through
a command line environment in a kubernetes pod.
The executor first saves each code block in a file in the working
directory, and then executes the code file in the container.
The executor executes the code blocks in the order they are received.
Currently, the executor only supports Python and shell scripts.
For Python code, use the language "python" for the code block.
For shell scripts, use the language "bash", "shell", or "sh" for the code
block.
Args:
image (_type_, optional): Docker image to use for code execution.
Defaults to "python:3-slim".
pod_name (Optional[str], optional): Name of the kubernetes pod
which is created. If None, will autogenerate a name. Defaults to None.
namespace (Optional[str], optional): Namespace of kubernetes pod
which is created. If None, will use current namespace of this instance
pod_spec (Optional[client.V1Pod], optional): Specification of kubernetes pod.
custom pod spec can be provided with this param.
if pod_spec is provided, params above(image, pod_name, namespace) are neglected.
container_name (Optional[str], optional): Name of the container where code block will be
executed. if pod_spec param is provided, container_name must be provided also.
timeout (int, optional): The timeout for code execution. Defaults to 60.
work_dir (Union[Path, str], optional): The working directory for the code
execution. Defaults to Path("/workspace").
kube_config_file (Optional[str], optional): kubernetes configuration file path.
If None, will use KUBECONFIG environment variables or service account token(incluster config)
stop_container (bool, optional): If true, will automatically stop the
container when stop is called, when the context manager exits or when
the Python process exits with atext. Defaults to True.
execution_policies (dict[str, bool], optional): defines supported execution language
Raises:
ValueError: On argument error, or if the container fails to start.
"""
if kube_config_file is None:
config.load_config()
else:
config.load_config(config_file=kube_config_file)
self._api_client = client.CoreV1Api()
if timeout < 1:
raise ValueError("Timeout must be greater than or equal to 1.")
self._timeout = timeout
if isinstance(work_dir, str):
work_dir = Path(work_dir)
self._work_dir: Path = work_dir
if container_name is None:
container_name = "autogen-code-exec"
self._container_name = container_name
# Start a container from the image, read to exec commands later
if pod_spec:
pod = pod_spec
else:
if pod_name is None:
pod_name = f"autogen-code-exec-{uuid.uuid4()}"
if namespace is None:
namespace_path = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
if not Path(namespace_path).is_file():
raise ValueError("Namespace where the pod will be launched must be provided")
with open(namespace_path, "r") as f:
namespace = f.read()
pod = client.V1Pod(
metadata=client.V1ObjectMeta(name=pod_name, namespace=namespace),
spec=client.V1PodSpec(
restart_policy="Never",
containers=[
client.V1Container(
args=["-c", "while true;do sleep 5; done"],
command=["/bin/sh"],
name=container_name,
image=image,
)
],
),
)
try:
pod_name = pod.metadata.name
namespace = pod.metadata.namespace
self._pod = self._api_client.create_namespaced_pod(namespace=namespace, body=pod)
except ApiException as e:
raise ValueError(f"Creating pod failed: {e}")
self._wait_for_ready()
def cleanup() -> None:
try:
self._api_client.delete_namespaced_pod(pod_name, namespace)
except ApiException:
pass
atexit.unregister(cleanup)
self._cleanup = cleanup
if stop_container:
atexit.register(cleanup)
self.execution_policies = self.DEFAULT_EXECUTION_POLICY.copy()
if execution_policies is not None:
self.execution_policies.update(execution_policies)
def _wait_for_ready(self, stop_time: float = 0.1) -> None:
elapsed_time = 0.0
name = self._pod.metadata.name
namespace = self._pod.metadata.namespace
while True:
sleep(stop_time)
elapsed_time += stop_time
if elapsed_time > self._timeout:
raise ValueError(
f"pod name {name} on namespace {namespace} is not Ready after timeout {self._timeout} seconds"
)
try:
pod_status = self._api_client.read_namespaced_pod_status(name, namespace)
if pod_status.status.phase == "Running":
break
except ApiException as e:
raise ValueError(f"reading pod status failed: {e}")
@property
def timeout(self) -> int:
"""(Experimental) The timeout for code execution."""
return self._timeout
@property
def work_dir(self) -> Path:
"""(Experimental) The working directory for the code execution."""
return self._work_dir
@property
def code_extractor(self) -> CodeExtractor:
"""(Experimental) Export a code extractor that can be used by an agent."""
return MarkdownCodeExtractor()
def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandLineCodeResult:
"""(Experimental) Execute the code blocks and return the result.
Args:
code_blocks (List[CodeBlock]): The code blocks to execute.
Returns:
CommandlineCodeResult: The result of the code execution."""
if len(code_blocks) == 0:
raise ValueError("No code blocks to execute.")
outputs = []
files = []
last_exit_code = 0
for code_block in code_blocks:
lang = self.LANGUAGE_ALIASES.get(code_block.language.lower(), code_block.language.lower())
if lang not in self.DEFAULT_EXECUTION_POLICY:
outputs.append(f"Unsupported language {lang}\n")
last_exit_code = 1
break
execute_code = self.execution_policies.get(lang, False)
code = silence_pip(code_block.code, lang)
if lang in ["bash", "shell", "sh"]:
code = "\n".join(["#!/bin/bash", code])
try:
filename = _get_file_name_from_content(code, self._work_dir)
except ValueError:
outputs.append("Filename is not in the workspace")
last_exit_code = 1
break
if not filename:
extension = self.LANGUAGE_FILE_EXTENSION.get(lang, lang)
filename = f"tmp_code_{md5(code.encode()).hexdigest()}.{extension}"
code_path = self._work_dir / filename
exec_script = textwrap.dedent(
"""
if [ ! -d "{workspace}" ]; then
mkdir {workspace}
fi
cat <<EOM >{code_path}\n
{code}
EOM
chmod +x {code_path}"""
)
exec_script = exec_script.format(workspace=str(self._work_dir), code_path=code_path, code=code)
stream(
self._api_client.connect_get_namespaced_pod_exec,
self._pod.metadata.name,
self._pod.metadata.namespace,
command=["/bin/sh", "-c", exec_script],
container=self._container_name,
stderr=True,
stdin=False,
stdout=True,
tty=False,
)
files.append(code_path)
if not execute_code:
outputs.append(f"Code saved to {str(code_path)}\n")
continue
resp = stream(
self._api_client.connect_get_namespaced_pod_exec,
self._pod.metadata.name,
self._pod.metadata.namespace,
command=["timeout", str(self._timeout), _cmd(lang), str(code_path)],
container=self._container_name,
stderr=True,
stdin=False,
stdout=True,
tty=False,
_preload_content=False,
)
stdout_messages = []
stderr_messages = []
while resp.is_open():
resp.update(timeout=1)
if resp.peek_stderr():
stderr_messages.append(resp.read_stderr())
if resp.peek_stdout():
stdout_messages.append(resp.read_stdout())
outputs.extend(stdout_messages + stderr_messages)
exit_code = resp.returncode
resp.close()
if exit_code == 124:
outputs.append("\n" + TIMEOUT_MSG)
last_exit_code = exit_code
if exit_code != 0:
break
code_file = str(files[0]) if files else None
return CommandLineCodeResult(exit_code=last_exit_code, output="".join(outputs), code_file=code_file)
def stop(self) -> None:
"""(Experimental) Stop the code executor."""
self._cleanup()
def __enter__(self) -> Self:
return self
def __exit__(
self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]
) -> None:
self.stop()

View File

@ -0,0 +1,532 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Building an Agent with Long-term Memory using Autogen and Zep\n",
"\n",
"This notebook walks through how to build an Autogen Agent with long-term memory. Zep builds a knowledge graph from user interactions with the agent, enabling the agent to recall relevant facts from previous conversations or user interactions.\n",
"\n",
"In this notebook we will:\n",
"- Create an Autogen Agent class that extends `ConversableAgent` by adding long-term memory\n",
"- Create a Mental Health Assistant Agent, CareBot, that acts as a counselor and coach.\n",
"- Create a user Agent, Cathy, who stands in for our expected user.\n",
"- Demonstrate preloading chat history into Zep.\n",
"- Demonstrate the agents in conversation, with CareBot recalling facts from previous conversations with Cathy.\n",
"- Inspect Facts within Zep, and demonstrate how to use Zep's Fact Ratings to improve the quality of returned facts.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Requirements\n",
"\n",
"````{=mdx}\n",
":::info Requirements\n",
"Some extra dependencies are needed for this notebook, which can be installed via pip:\n",
"\n",
"```bash\n",
"pip install autogen~=0.3 zep-cloud python-dotenv\n",
"```\n",
"\n",
"For more information, please refer to the [installation guide](/docs/installation/).\n",
":::\n",
"````"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"flaml.automl is not available. Please install flaml[automl] to enable AutoML functionalities.\n"
]
}
],
"source": [
"import os\n",
"import uuid\n",
"from typing import Dict, Union\n",
"\n",
"from dotenv import load_dotenv\n",
"\n",
"from autogen import Agent, ConversableAgent\n",
"\n",
"load_dotenv()\n",
"\n",
"config_list = [\n",
" {\n",
" \"model\": \"gpt-4o-mini\",\n",
" \"api_key\": os.environ.get(\"OPENAI_API_KEY\"),\n",
" \"max_tokens\": 1024,\n",
" }\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## initiualize the Zep Client\n",
"\n",
"You can sign up for a Zep account here: https://www.getzep.com/"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from zep_cloud import FactRatingExamples, FactRatingInstruction, Message\n",
"from zep_cloud.client import AsyncZep\n",
"\n",
"MIN_FACT_RATING = 0.3\n",
"\n",
"# Configure Zep\n",
"zep = AsyncZep(api_key=os.environ.get(\"ZEP_API_KEY\"))"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def convert_to_zep_messages(chat_history: list[dict[str, str | None]]) -> list[Message]:\n",
" \"\"\"\n",
" Convert chat history to Zep messages.\n",
"\n",
" Args:\n",
" chat_history (list): List of dictionaries containing chat messages.\n",
"\n",
" Returns:\n",
" list: List of Zep Message objects.\n",
" \"\"\"\n",
" return [\n",
" Message(\n",
" role_type=msg[\"role\"],\n",
" role=msg.get(\"name\", None),\n",
" content=msg[\"content\"],\n",
" )\n",
" for msg in chat_history\n",
" ]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## ZepConversableAgent\n",
"\n",
"The `ZepConversableAgent` is a custom implementation of the `ConversableAgent` that integrates with Zep for long-term memory management. This class extends the functionality of the base `ConversableAgent` by adding Zep-specific features for persisting and retrieving facts from long-term memory."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"class ZepConversableAgent(ConversableAgent):\n",
" \"\"\"\n",
" A custom ConversableAgent that integrates with Zep for long-term memory.\n",
" \"\"\"\n",
"\n",
" def __init__(\n",
" self,\n",
" name: str,\n",
" system_message: str,\n",
" llm_config: dict,\n",
" function_map: dict,\n",
" human_input_mode: str,\n",
" zep_session_id: str,\n",
" ):\n",
" super().__init__(\n",
" name=name,\n",
" system_message=system_message,\n",
" llm_config=llm_config,\n",
" function_map=function_map,\n",
" human_input_mode=human_input_mode,\n",
" )\n",
" self.zep_session_id = zep_session_id\n",
" # store the original system message as we will update it with relevant facts from Zep\n",
" self.original_system_message = system_message\n",
" self.register_hook(\"a_process_last_received_message\", self.persist_user_messages)\n",
" self.register_hook(\"a_process_message_before_send\", self.persist_assistant_messages)\n",
"\n",
" async def persist_assistant_messages(\n",
" self, sender: Agent, message: Union[Dict, str], recipient: Agent, silent: bool\n",
" ):\n",
" \"\"\"Agent sends a message to the user. Add the message to Zep.\"\"\"\n",
"\n",
" # Assume message is a string\n",
" zep_messages = convert_to_zep_messages([{\"role\": \"assistant\", \"name\": self.name, \"content\": message}])\n",
" await zep.memory.add(session_id=self.zep_session_id, messages=zep_messages)\n",
"\n",
" return message\n",
"\n",
" async def persist_user_messages(self, messages: list[dict[str, str]] | str):\n",
" \"\"\"\n",
" User sends a message to the agent. Add the message to Zep and\n",
" update the system message with relevant facts from Zep.\n",
" \"\"\"\n",
" # Assume messages is a string\n",
" zep_messages = convert_to_zep_messages([{\"role\": \"user\", \"content\": messages}])\n",
" await zep.memory.add(session_id=self.zep_session_id, messages=zep_messages)\n",
"\n",
" memory = await zep.memory.get(self.zep_session_id, min_rating=MIN_FACT_RATING)\n",
"\n",
" # Update the system message with the relevant facts retrieved from Zep\n",
" self.update_system_message(\n",
" self.original_system_message\n",
" + f\"\\n\\nRelevant facts about the user and their prior conversation:\\n{memory.relevant_facts}\"\n",
" )\n",
"\n",
" return messages"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Zep User and Session Management\n",
"\n",
"### Zep User\n",
"A Zep User represents an individual interacting with your application. Each User can have multiple Sessions associated with them, allowing you to track and manage interactions over time. The unique identifier for each user is their `UserID`, which can be any string value (e.g., username, email address, or UUID).\n",
"\n",
"### Zep Session\n",
"A Session represents a conversation and can be associated with Users in a one-to-many relationship. Chat messages are added to Sessions, with each session having many messages.\n",
"\n",
"### Fact Rating\n",
" \n",
"Fact Rating is a feature in Zep that allows you to rate the importance or relevance of facts extracted from conversations. This helps in prioritizing and filtering information when retrieving memory artifacts. Here, we rate facts based on poignancy. We provide a definition of poignancy and several examples of highly poignant and low-poignancy facts. When retrieving memory, you can use the `min_rating` parameter to filter facts based on their importance.\n",
" \n",
"Fact Rating helps ensure the most relevant information, especially in long or complex conversations, is used to ground the agent.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Session(classifications=None, created_at='2024-10-07T21:12:13.952672Z', deleted_at=None, ended_at=None, fact_rating_instruction=FactRatingInstruction(examples=FactRatingExamples(high=\"The user received news of a family member's serious illness.\", low='The user bought a new brand of toothpaste.', medium='The user completed a challenging marathon.'), instruction='Rate the facts by poignancy. Highly poignant \\nfacts have a significant emotional impact or relevance to the user. \\nLow poignant facts are minimally relevant or of little emotional \\nsignificance.'), fact_version_uuid=None, facts=None, id=774, metadata=None, project_uuid='00000000-0000-0000-0000-000000000000', session_id='f3854ad0-5bd4-4814-a814-ec0880817953', updated_at='2024-10-07T21:12:13.952672Z', user_id='Cathy1023', uuid_='31ab3314-5ac8-4361-ad11-848fb7befedf')"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bot_name = \"CareBot\"\n",
"user_name = \"Cathy\"\n",
"\n",
"user_id = user_name + str(uuid.uuid4())[:4]\n",
"session_id = str(uuid.uuid4())\n",
"\n",
"await zep.user.add(user_id=user_id)\n",
"\n",
"fact_rating_instruction = \"\"\"Rate the facts by poignancy. Highly poignant\n",
" facts have a significant emotional impact or relevance to the user.\n",
" Low poignant facts are minimally relevant or of little emotional significance.\n",
"\"\"\"\n",
"\n",
"fact_rating_examples = FactRatingExamples(\n",
" high=\"The user received news of a family member's serious illness.\",\n",
" medium=\"The user completed a challenging marathon.\",\n",
" low=\"The user bought a new brand of toothpaste.\",\n",
")\n",
"\n",
"await zep.memory.add_session(\n",
" user_id=user_id,\n",
" session_id=session_id,\n",
" fact_rating_instruction=FactRatingInstruction(\n",
" instruction=fact_rating_instruction,\n",
" examples=fact_rating_examples,\n",
" ),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Preload a prior conversation into Zep\n",
"\n",
"We'll load a prior conversation into long-term memory. We'll use facts derived from this conversation when Cathy restarts the conversation with CareBot, ensuring Carebot has context."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"SuccessResponse(message='OK')"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"chat_history = [\n",
" {\n",
" \"role\": \"assistant\",\n",
" \"name\": \"carebot\",\n",
" \"content\": \"Hi Cathy, how are you doing today?\",\n",
" },\n",
" {\n",
" \"role\": \"user\",\n",
" \"name\": \"Cathy\",\n",
" \"content\": \"To be honest, I've been feeling a bit down and demotivated lately. It's been tough.\",\n",
" },\n",
" {\n",
" \"role\": \"assistant\",\n",
" \"name\": \"CareBot\",\n",
" \"content\": \"I'm sorry to hear that you're feeling down and demotivated, Cathy. It's understandable given the challenges you're facing. Can you tell me more about what's been going on?\",\n",
" },\n",
" {\n",
" \"role\": \"user\",\n",
" \"name\": \"Cathy\",\n",
" \"content\": \"Well, I'm really struggling to process the passing of my mother.\",\n",
" },\n",
" {\n",
" \"role\": \"assistant\",\n",
" \"name\": \"CareBot\",\n",
" \"content\": \"I'm deeply sorry for your loss, Cathy. Losing a parent is incredibly difficult. It's normal to struggle with grief, and there's no 'right' way to process it. Would you like to talk about your mother or how you're coping?\",\n",
" },\n",
" {\n",
" \"role\": \"user\",\n",
" \"name\": \"Cathy\",\n",
" \"content\": \"Yes, I'd like to talk about my mother. She was a kind and loving person.\",\n",
" },\n",
"]\n",
"\n",
"# Convert chat history to Zep messages\n",
"zep_messages = convert_to_zep_messages(chat_history)\n",
"\n",
"await zep.memory.add(session_id=session_id, messages=zep_messages)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Review all facts in Zep\n",
"\n",
"We query all session facts for this user session. Only facts that meet the `MIN_FACT_RATING` threshold are returned."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"created_at='2024-10-07T21:12:15.96584Z' fact='Cathy describes her mother as a kind and loving person.' rating=0.5 uuid_='6a086a73-d4b8-4c1b-9b2f-08d5d326d813'\n",
"created_at='2024-10-07T21:12:15.96584Z' fact='Cathy has been feeling down and demotivated lately.' rating=0.5 uuid_='e19d959c-2a01-4cc7-9d49-108719f1a749'\n",
"created_at='2024-10-07T21:12:15.96584Z' fact='Cathy is struggling to process the passing of her mother.' rating=0.75 uuid_='d6c12a5d-d2a0-486e-b25d-3d4bdc5ff466'\n"
]
}
],
"source": [
"response = await zep.memory.get_session_facts(session_id=session_id, min_rating=MIN_FACT_RATING)\n",
"\n",
"for r in response.facts:\n",
" print(r)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create the Autogen agent, CareBot, an instance of `ZepConversableAgent`\n",
"\n",
"We pass in the current `session_id` into the CareBot agent which allows it to retrieve relevant facts related to the conversation with Cathy."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"carebot_system_message = \"\"\"\n",
"You are a compassionate mental health bot and caregiver. Review information about the user and their prior conversation below and respond accordingly.\n",
"Keep responses empathetic and supportive. And remember, always prioritize the user's well-being and mental health. Keep your responses very concise and to the point.\n",
"\"\"\"\n",
"\n",
"agent = ZepConversableAgent(\n",
" bot_name,\n",
" system_message=carebot_system_message,\n",
" llm_config={\"config_list\": config_list},\n",
" function_map=None, # No registered functions, by default it is None.\n",
" human_input_mode=\"NEVER\", # Never ask for human input.\n",
" zep_session_id=session_id,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create the Autogen agent, Cathy\n",
"\n",
"Cathy is a stand-in for a human. When building a production application, you'd replace Cathy with a human-in-the-loop pattern.\n",
"\n",
"**Note** that we're instructing Cathy to start the conversation with CareBit by asking about her previous session. This is an opportunity for us to test whether fact retrieval from Zep's long-term memory is working. "
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"cathy = ConversableAgent(\n",
" user_name,\n",
" system_message=\"You are returning to your conversation with CareBot, a mental health bot. Ask the bot about your previous session.\",\n",
" llm_config={\"config_list\": config_list},\n",
" human_input_mode=\"NEVER\", # Never ask for human input.\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Start the conversation\n",
"\n",
"We use Autogen's `a_initiate_chat` method to get the two agents conversing. CareBot is the primary agent.\n",
"\n",
"**NOTE** how Carebot is able to recall the past conversation about Cathy's mother in detail, having had relevant facts from Zep added to its system prompt."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"result = await agent.a_initiate_chat(\n",
" cathy,\n",
" message=\"Hi Cathy, nice to see you again. How are you doing today?\",\n",
" max_turns=3,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Review current facts in Zep\n",
"\n",
"Let's see how the facts have evolved as the conversation has progressed."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"created_at='2024-10-07T20:04:28.397184Z' fact=\"Cathy wants to reflect on a previous conversation about her mother and explore the topic of her mother's passing further.\" rating=0.75 uuid_='56488eeb-d8ac-4b2f-8acc-75f71b56ad76'\n",
"created_at='2024-10-07T20:04:28.397184Z' fact='Cathy is struggling to process the passing of her mother and has been feeling down and demotivated lately.' rating=0.75 uuid_='0fea3f05-ed1a-4e39-a092-c91f8af9e501'\n",
"created_at='2024-10-07T20:04:28.397184Z' fact='Cathy describes her mother as a kind and loving person.' rating=0.5 uuid_='131de203-2984-4cba-9aef-e500611f06d9'\n"
]
}
],
"source": [
"response = await zep.memory.get_session_facts(session_id, min_rating=MIN_FACT_RATING)\n",
"\n",
"for r in response.facts:\n",
" print(r)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Search over Facts in Zep's long-term memory\n",
"\n",
"In addition to the `memory.get` method which uses the current conversation to retrieve facts, we can also search Zep with our own keywords. Here, we retrieve facts using a query. Again, we use fact ratings to limit the returned facts to only those with a high poignancy rating.\n",
"\n",
"The `memory.search_sessions` API may be used as an Agent tool, enabling an agent to search across user memory for relevant facts."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"created_at='2024-10-07T20:04:28.397184Z' fact='Cathy describes her mother as a kind and loving person.' rating=0.5 uuid_='131de203-2984-4cba-9aef-e500611f06d9'\n",
"created_at='2024-10-07T20:04:28.397184Z' fact='Cathy is struggling to process the passing of her mother and has been feeling down and demotivated lately.' rating=0.75 uuid_='0fea3f05-ed1a-4e39-a092-c91f8af9e501'\n",
"created_at='2024-10-07T20:04:28.397184Z' fact=\"Cathy wants to reflect on a previous conversation about her mother and explore the topic of her mother's passing further.\" rating=0.75 uuid_='56488eeb-d8ac-4b2f-8acc-75f71b56ad76'\n"
]
}
],
"source": [
"response = await zep.memory.search_sessions(\n",
" text=\"What do you know about Cathy's family?\",\n",
" user_id=user_id,\n",
" search_scope=\"facts\",\n",
" min_fact_rating=MIN_FACT_RATING,\n",
")\n",
"\n",
"for r in response.results:\n",
" print(r.fact)"
]
}
],
"metadata": {
"front_matter": {
"tags": [
"memory"
],
"description": "Agent Memory with Zep."
},
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,579 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Using RetrieveChat Powered by Couchbase Capella for Retrieve Augmented Code Generation and Question Answering\n",
"\n",
"AutoGen offers conversable agents powered by LLM, tool or human, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participation through multi-agent conversation.\n",
"Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n",
"\n",
"RetrieveChat is a conversational system for retrieval-augmented code generation and question answering. In this notebook, we demonstrate how to utilize RetrieveChat to generate code and answer questions based on customized documentations that are not present in the LLM's training dataset. RetrieveChat uses the `AssistantAgent` and `RetrieveUserProxyAgent`, which is similar to the usage of `AssistantAgent` and `UserProxyAgent` in other notebooks (e.g., [Automated Task Solving with Code Generation, Execution & Debugging](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_auto_feedback_from_code_execution.ipynb)). Essentially, `RetrieveUserProxyAgent` implement a different auto-reply mechanism corresponding to the RetrieveChat prompts.\n",
"\n",
"## Table of Contents\n",
"We'll demonstrate six examples of using RetrieveChat for code generation and question answering:\n",
"\n",
"- [Example 1: Generate code based off docstrings w/o human feedback](#example-1)\n",
"\n",
"````{=mdx}\n",
":::info Requirements\n",
"Some extra dependencies are needed for this notebook, which can be installed via pip:\n",
"\n",
"```bash\n",
"pip install pyautogen[retrievechat-couchbase] flaml[automl]\n",
"```\n",
"\n",
"For more information, please refer to the [installation guide](/docs/installation/).\n",
":::\n",
"````\n",
"\n",
"Ensure you have a Couchbase Capella cluster running. Read more on how to get started [here](https://docs.couchbase.com/cloud/get-started/intro.html)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Set your API Endpoint\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"models to use: ['gpt-4o-mini']\n"
]
}
],
"source": [
"import os\n",
"import sys\n",
"\n",
"from autogen import AssistantAgent\n",
"\n",
"sys.path.append(os.path.abspath(\"/workspaces/autogen/autogen/agentchat/contrib\"))\n",
"\n",
"from autogen.agentchat.contrib.retrieve_user_proxy_agent import RetrieveUserProxyAgent\n",
"\n",
"# Accepted file formats for that can be stored in\n",
"# a vector database instance\n",
"from autogen.retrieve_utils import TEXT_FORMATS\n",
"\n",
"config_list = [{\"model\": \"gpt-4o-mini\", \"api_key\": os.environ[\"OPENAI_API_KEY\"], \"api_type\": \"openai\"}]\n",
"assert len(config_list) > 0\n",
"print(\"models to use: \", [config_list[i][\"model\"] for i in range(len(config_list))])"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"````{=mdx}\n",
":::tip\n",
"Learn more about configuring LLMs for agents [here](/docs/topics/llm_configuration).\n",
":::\n",
"````\n",
"\n",
"## Construct agents for RetrieveChat\n",
"\n",
"We start by initializing the `AssistantAgent` and `RetrieveUserProxyAgent`. The system message needs to be set to \"You are a helpful assistant.\" for AssistantAgent. The detailed instructions are given in the user message. Later we will use the `RetrieveUserProxyAgent.message_generator` to combine the instructions and a retrieval augmented generation task for an initial prompt to be sent to the LLM assistant."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Accepted file formats for `docs_path`:\n",
"['txt', 'json', 'csv', 'tsv', 'md', 'html', 'htm', 'rtf', 'rst', 'jsonl', 'log', 'xml', 'yaml', 'yml', 'pdf']\n"
]
}
],
"source": [
"print(\"Accepted file formats for `docs_path`:\")\n",
"print(TEXT_FORMATS)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 1. create an AssistantAgent instance named \"assistant\"\n",
"assistant = AssistantAgent(\n",
" name=\"assistant\",\n",
" system_message=\"You are a helpful assistant.\",\n",
" llm_config={\n",
" \"timeout\": 600,\n",
" \"cache_seed\": 42,\n",
" \"config_list\": config_list,\n",
" },\n",
")\n",
"\n",
"# 2. create the RetrieveUserProxyAgent instance named \"ragproxyagent\"\n",
"# Refer to https://microsoft.github.io/autogen/docs/reference/agentchat/contrib/retrieve_user_proxy_agent\n",
"# and https://microsoft.github.io/autogen/docs/reference/agentchat/contrib/vectordb/couchbase\n",
"# for more information on the RetrieveUserProxyAgent and CouchbaseVectorDB\n",
"ragproxyagent = RetrieveUserProxyAgent(\n",
" name=\"ragproxyagent\",\n",
" human_input_mode=\"NEVER\",\n",
" max_consecutive_auto_reply=3,\n",
" retrieve_config={\n",
" \"task\": \"code\",\n",
" \"docs_path\": [\n",
" \"https://raw.githubusercontent.com/microsoft/FLAML/main/website/docs/Examples/Integrate%20-%20Spark.md\",\n",
" \"https://raw.githubusercontent.com/microsoft/FLAML/main/website/docs/Research.md\",\n",
" ],\n",
" \"chunk_token_size\": 2000,\n",
" \"model\": config_list[0][\"model\"],\n",
" \"vector_db\": \"couchbase\", # Couchbase Capella VectorDB\n",
" \"collection_name\": \"demo_collection\", # Couchbase Capella collection name to be utilized/created\n",
" \"db_config\": {\n",
" \"connection_string\": os.environ[\"CB_CONN_STR\"], # Couchbase Capella connection string\n",
" \"username\": os.environ[\"CB_USERNAME\"], # Couchbase Capella username\n",
" \"password\": os.environ[\"CB_PASSWORD\"], # Couchbase Capella password\n",
" \"bucket_name\": \"test_db\", # Couchbase Capella bucket name\n",
" \"scope_name\": \"test_scope\", # Couchbase Capella scope name\n",
" \"index_name\": \"vector_index\", # Couchbase Capella index name to be created\n",
" },\n",
" \"get_or_create\": True, # set to False if you don't want to reuse an existing collection\n",
" \"overwrite\": False, # set to True if you want to overwrite an existing collection, each overwrite will force a index creation and reupload of documents\n",
" },\n",
" code_execution_config=False, # set to False if you don't want to execute the code\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Example 1\n",
"\n",
"[Back to top](#table-of-contents)\n",
"\n",
"Use RetrieveChat to help generate sample code and automatically run the code and fix errors if there is any.\n",
"\n",
"Problem: Which API should I use if I want to use FLAML for a classification task and I want to train the model in 30 seconds. Use spark to parallel the training. Force cancel jobs if time limit is reached.\n",
"\n",
"Note: You may need to create an index on the cluster to query"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-16 12:08:07,062 - autogen.agentchat.contrib.retrieve_user_proxy_agent - INFO - \u001b[32mUse the existing collection `demo_collection`.\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Trying to create collection.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-16 12:08:07,953 - autogen.agentchat.contrib.retrieve_user_proxy_agent - INFO - Found 2 chunks.\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"VectorDB returns doc_ids: [['bdfbc921', '7968cf3c']]\n",
"\u001b[32mAdding content of doc bdfbc921 to context.\u001b[0m\n",
"\u001b[32mAdding content of doc 7968cf3c to context.\u001b[0m\n",
"\u001b[33mragproxyagent\u001b[0m (to assistant):\n",
"\n",
"You're a retrieve augmented coding assistant. You answer user's questions based on your own knowledge and the\n",
"context provided by the user.\n",
"If you can't answer the question with or without the current context, you should reply exactly `UPDATE CONTEXT`.\n",
"For code generation, you must obey the following rules:\n",
"Rule 1. You MUST NOT install any packages because all the packages needed are already installed.\n",
"Rule 2. You must follow the formats below to write your code:\n",
"```language\n",
"# your code\n",
"```\n",
"\n",
"User's question is: How can I use FLAML to perform a classification task and use spark to do parallel training. Train 30 seconds and force cancel jobs if time limit is reached.\n",
"\n",
"Context is: # Integrate - Spark\n",
"\n",
"FLAML has integrated Spark for distributed training. There are two main aspects of integration with Spark:\n",
"\n",
"- Use Spark ML estimators for AutoML.\n",
"- Use Spark to run training in parallel spark jobs.\n",
"\n",
"## Spark ML Estimators\n",
"\n",
"FLAML integrates estimators based on Spark ML models. These models are trained in parallel using Spark, so we called them Spark estimators. To use these models, you first need to organize your data in the required format.\n",
"\n",
"### Data\n",
"\n",
"For Spark estimators, AutoML only consumes Spark data. FLAML provides a convenient function `to_pandas_on_spark` in the `flaml.automl.spark.utils` module to convert your data into a pandas-on-spark (`pyspark.pandas`) dataframe/series, which Spark estimators require.\n",
"\n",
"This utility function takes data in the form of a `pandas.Dataframe` or `pyspark.sql.Dataframe` and converts it into a pandas-on-spark dataframe. It also takes `pandas.Series` or `pyspark.sql.Dataframe` and converts it into a [pandas-on-spark](https://spark.apache.org/docs/latest/api/python/user_guide/pandas_on_spark/index.html) series. If you pass in a `pyspark.pandas.Dataframe`, it will not make any changes.\n",
"\n",
"This function also accepts optional arguments `index_col` and `default_index_type`.\n",
"\n",
"- `index_col` is the column name to use as the index, default is None.\n",
"- `default_index_type` is the default index type, default is \"distributed-sequence\". More info about default index type could be found on Spark official [documentation](https://spark.apache.org/docs/latest/api/python/user_guide/pandas_on_spark/options.html#default-index-type)\n",
"\n",
"Here is an example code snippet for Spark Data:\n",
"\n",
"```python\n",
"import pandas as pd\n",
"from flaml.automl.spark.utils import to_pandas_on_spark\n",
"\n",
"# Creating a dictionary\n",
"data = {\n",
" \"Square_Feet\": [800, 1200, 1800, 1500, 850],\n",
" \"Age_Years\": [20, 15, 10, 7, 25],\n",
" \"Price\": [100000, 200000, 300000, 240000, 120000],\n",
"}\n",
"\n",
"# Creating a pandas DataFrame\n",
"dataframe = pd.DataFrame(data)\n",
"label = \"Price\"\n",
"\n",
"# Convert to pandas-on-spark dataframe\n",
"psdf = to_pandas_on_spark(dataframe)\n",
"```\n",
"\n",
"To use Spark ML models you need to format your data appropriately. Specifically, use [`VectorAssembler`](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.ml.feature.VectorAssembler.html) to merge all feature columns into a single vector column.\n",
"\n",
"Here is an example of how to use it:\n",
"\n",
"```python\n",
"from pyspark.ml.feature import VectorAssembler\n",
"\n",
"columns = psdf.columns\n",
"feature_cols = [col for col in columns if col != label]\n",
"featurizer = VectorAssembler(inputCols=feature_cols, outputCol=\"features\")\n",
"psdf = featurizer.transform(psdf.to_spark(index_col=\"index\"))[\"index\", \"features\"]\n",
"```\n",
"\n",
"Later in conducting the experiment, use your pandas-on-spark data like non-spark data and pass them using `X_train, y_train` or `dataframe, label`.\n",
"\n",
"### Estimators\n",
"\n",
"#### Model List\n",
"\n",
"- `lgbm_spark`: The class for fine-tuning Spark version LightGBM models, using [SynapseML](https://microsoft.github.io/SynapseML/docs/features/lightgbm/about/) API.\n",
"\n",
"#### Usage\n",
"\n",
"First, prepare your data in the required format as described in the previous section.\n",
"\n",
"By including the models you intend to try in the `estimators_list` argument to `flaml.automl`, FLAML will start trying configurations for these models. If your input is Spark data, FLAML will also use estimators with the `_spark` postfix by default, even if you haven't specified them.\n",
"\n",
"Here is an example code snippet using SparkML models in AutoML:\n",
"\n",
"```python\n",
"import flaml\n",
"\n",
"# prepare your data in pandas-on-spark format as we previously mentioned\n",
"\n",
"automl = flaml.AutoML()\n",
"settings = {\n",
" \"time_budget\": 30,\n",
" \"metric\": \"r2\",\n",
" \"estimator_list\": [\"lgbm_spark\"], # this setting is optional\n",
" \"task\": \"regression\",\n",
"}\n",
"\n",
"automl.fit(\n",
" dataframe=psdf,\n",
" label=label,\n",
" **settings,\n",
")\n",
"```\n",
"\n",
"[Link to notebook](https://github.com/microsoft/FLAML/blob/main/notebook/automl_bankrupt_synapseml.ipynb) | [Open in colab](https://colab.research.google.com/github/microsoft/FLAML/blob/main/notebook/automl_bankrupt_synapseml.ipynb)\n",
"\n",
"## Parallel Spark Jobs\n",
"\n",
"You can activate Spark as the parallel backend during parallel tuning in both [AutoML](/docs/Use-Cases/Task-Oriented-AutoML#parallel-tuning) and [Hyperparameter Tuning](/docs/Use-Cases/Tune-User-Defined-Function#parallel-tuning), by setting the `use_spark` to `true`. FLAML will dispatch your job to the distributed Spark backend using [`joblib-spark`](https://github.com/joblib/joblib-spark).\n",
"\n",
"Please note that you should not set `use_spark` to `true` when applying AutoML and Tuning for Spark Data. This is because only SparkML models will be used for Spark Data in AutoML and Tuning. As SparkML models run in parallel, there is no need to distribute them with `use_spark` again.\n",
"\n",
"All the Spark-related arguments are stated below. These arguments are available in both Hyperparameter Tuning and AutoML:\n",
"\n",
"- `use_spark`: boolean, default=False | Whether to use spark to run the training in parallel spark jobs. This can be used to accelerate training on large models and large datasets, but will incur more overhead in time and thus slow down training in some cases. GPU training is not supported yet when use_spark is True. For Spark clusters, by default, we will launch one trial per executor. However, sometimes we want to launch more trials than the number of executors (e.g., local mode). In this case, we can set the environment variable `FLAML_MAX_CONCURRENT` to override the detected `num_executors`. The final number of concurrent trials will be the minimum of `n_concurrent_trials` and `num_executors`.\n",
"- `n_concurrent_trials`: int, default=1 | The number of concurrent trials. When n_concurrent_trials > 1, FLAML performes parallel tuning.\n",
"- `force_cancel`: boolean, default=False | Whether to forcely cancel Spark jobs if the search time exceeded the time budget. Spark jobs include parallel tuning jobs and Spark-based model training jobs.\n",
"\n",
"An example code snippet for using parallel Spark jobs:\n",
"\n",
"```python\n",
"import flaml\n",
"\n",
"automl_experiment = flaml.AutoML()\n",
"automl_settings = {\n",
" \"time_budget\": 30,\n",
" \"metric\": \"r2\",\n",
" \"task\": \"regression\",\n",
" \"n_concurrent_trials\": 2,\n",
" \"use_spark\": True,\n",
" \"force_cancel\": True, # Activating the force_cancel option can immediately halt Spark jobs once they exceed the allocated time_budget.\n",
"}\n",
"\n",
"automl.fit(\n",
" dataframe=dataframe,\n",
" label=label,\n",
" **automl_settings,\n",
")\n",
"```\n",
"\n",
"[Link to notebook](https://github.com/microsoft/FLAML/blob/main/notebook/integrate_spark.ipynb) | [Open in colab](https://colab.research.google.com/github/microsoft/FLAML/blob/main/notebook/integrate_spark.ipynb)\n",
"# Research\n",
"\n",
"For technical details, please check our research publications.\n",
"\n",
"- [FLAML: A Fast and Lightweight AutoML Library](https://www.microsoft.com/en-us/research/publication/flaml-a-fast-and-lightweight-automl-library/). Chi Wang, Qingyun Wu, Markus Weimer, Erkang Zhu. MLSys 2021.\n",
"\n",
"```bibtex\n",
"@inproceedings{wang2021flaml,\n",
" title={FLAML: A Fast and Lightweight AutoML Library},\n",
" author={Chi Wang and Qingyun Wu and Markus Weimer and Erkang Zhu},\n",
" year={2021},\n",
" booktitle={MLSys},\n",
"}\n",
"```\n",
"\n",
"- [Frugal Optimization for Cost-related Hyperparameters](https://arxiv.org/abs/2005.01571). Qingyun Wu, Chi Wang, Silu Huang. AAAI 2021.\n",
"\n",
"```bibtex\n",
"@inproceedings{wu2021cfo,\n",
" title={Frugal Optimization for Cost-related Hyperparameters},\n",
" author={Qingyun Wu and Chi Wang and Silu Huang},\n",
" year={2021},\n",
" booktitle={AAAI},\n",
"}\n",
"```\n",
"\n",
"- [Economical Hyperparameter Optimization With Blended Search Strategy](https://www.microsoft.com/en-us/research/publication/economical-hyperparameter-optimization-with-blended-search-strategy/). Chi Wang, Qingyun Wu, Silu Huang, Amin Saied. ICLR 2021.\n",
"\n",
"```bibtex\n",
"@inproceedings{wang2021blendsearch,\n",
" title={Economical Hyperparameter Optimization With Blended Search Strategy},\n",
" author={Chi Wang and Qingyun Wu and Silu Huang and Amin Saied},\n",
" year={2021},\n",
" booktitle={ICLR},\n",
"}\n",
"```\n",
"\n",
"- [An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models](https://aclanthology.org/2021.acl-long.178.pdf). Susan Xueqing Liu, Chi Wang. ACL 2021.\n",
"\n",
"```bibtex\n",
"@inproceedings{liuwang2021hpolm,\n",
" title={An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models},\n",
" author={Susan Xueqing Liu and Chi Wang},\n",
" year={2021},\n",
" booktitle={ACL},\n",
"}\n",
"```\n",
"\n",
"- [ChaCha for Online AutoML](https://www.microsoft.com/en-us/research/publication/chacha-for-online-automl/). Qingyun Wu, Chi Wang, John Langford, Paul Mineiro and Marco Rossi. ICML 2021.\n",
"\n",
"```bibtex\n",
"@inproceedings{wu2021chacha,\n",
" title={ChaCha for Online AutoML},\n",
" author={Qingyun Wu and Chi Wang and John Langford and Paul Mineiro and Marco Rossi},\n",
" year={2021},\n",
" booktitle={ICML},\n",
"}\n",
"```\n",
"\n",
"- [Fair AutoML](https://arxiv.org/abs/2111.06495). Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2111.06495 (2021).\n",
"\n",
"```bibtex\n",
"@inproceedings{wuwang2021fairautoml,\n",
" title={Fair AutoML},\n",
" author={Qingyun Wu and Chi Wang},\n",
" year={2021},\n",
" booktitle={ArXiv preprint arXiv:2111.06495},\n",
"}\n",
"```\n",
"\n",
"- [Mining Robust Default Configurations for Resource-constrained AutoML](https://arxiv.org/abs/2202.09927). Moe Kayali, Chi Wang. ArXiv preprint arXiv:2202.09927 (2022).\n",
"\n",
"```bibtex\n",
"@inproceedings{kayaliwang2022default,\n",
" title={Mining Robust Default Configurations for Resource-constrained AutoML},\n",
" author={Moe Kayali and Chi Wang},\n",
" year={2022},\n",
" booktitle={ArXiv preprint arXiv:2202.09927},\n",
"}\n",
"```\n",
"\n",
"- [Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives](https://openreview.net/forum?id=0Ij9_q567Ma). Shaokun Zhang, Feiran Jia, Chi Wang, Qingyun Wu. ICLR 2023 (notable-top-5%).\n",
"\n",
"```bibtex\n",
"@inproceedings{zhang2023targeted,\n",
" title={Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives},\n",
" author={Shaokun Zhang and Feiran Jia and Chi Wang and Qingyun Wu},\n",
" booktitle={International Conference on Learning Representations},\n",
" year={2023},\n",
" url={https://openreview.net/forum?id=0Ij9_q567Ma},\n",
"}\n",
"```\n",
"\n",
"- [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673). Chi Wang, Susan Xueqing Liu, Ahmed H. Awadallah. ArXiv preprint arXiv:2303.04673 (2023).\n",
"\n",
"```bibtex\n",
"@inproceedings{wang2023EcoOptiGen,\n",
" title={Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference},\n",
" author={Chi Wang and Susan Xueqing Liu and Ahmed H. Awadallah},\n",
" year={2023},\n",
" booktitle={ArXiv preprint arXiv:2303.04673},\n",
"}\n",
"```\n",
"\n",
"- [An Empirical Study on Challenging Math Problem Solving with GPT-4](https://arxiv.org/abs/2306.01337). Yiran Wu, Feiran Jia, Shaokun Zhang, Hangyu Li, Erkang Zhu, Yue Wang, Yin Tat Lee, Richard Peng, Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2306.01337 (2023).\n",
"\n",
"```bibtex\n",
"@inproceedings{wu2023empirical,\n",
" title={An Empirical Study on Challenging Math Problem Solving with GPT-4},\n",
" author={Yiran Wu and Feiran Jia and Shaokun Zhang and Hangyu Li and Erkang Zhu and Yue Wang and Yin Tat Lee and Richard Peng and Qingyun Wu and Chi Wang},\n",
" year={2023},\n",
" booktitle={ArXiv preprint arXiv:2306.01337},\n",
"}\n",
"```\n",
"\n",
"\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33massistant\u001b[0m (to ragproxyagent):\n",
"\n",
"```python\n",
"import pandas as pd\n",
"from pyspark.ml.feature import VectorAssembler\n",
"import flaml\n",
"from flaml.automl.spark.utils import to_pandas_on_spark\n",
"\n",
"# Creating a dictionary for the example data\n",
"data = {\n",
" \"Square_Feet\": [800, 1200, 1800, 1500, 850],\n",
" \"Age_Years\": [20, 15, 10, 7, 25],\n",
" \"Price\": [100000, 200000, 300000, 240000, 120000],\n",
"}\n",
"\n",
"# Creating a pandas DataFrame\n",
"dataframe = pd.DataFrame(data)\n",
"label = \"Price\"\n",
"\n",
"# Convert to pandas-on-spark dataframe\n",
"psdf = to_pandas_on_spark(dataframe)\n",
"\n",
"# Prepare features using VectorAssembler\n",
"columns = psdf.columns\n",
"feature_cols = [col for col in columns if col != label]\n",
"featurizer = VectorAssembler(inputCols=feature_cols, outputCol=\"features\")\n",
"psdf = featurizer.transform(psdf.to_spark(index_col=\"index\"))[[\"index\", \"features\"]]\n",
"\n",
"# Setting up and running FLAML for AutoML with Spark\n",
"automl = flaml.AutoML()\n",
"automl_settings = {\n",
" \"time_budget\": 30, # Set the time budget to 30 seconds\n",
" \"metric\": \"r2\", # Performance metric\n",
" \"task\": \"regression\", # Problem type\n",
" \"n_concurrent_trials\": 2, # Number of concurrent trials\n",
" \"use_spark\": True, # Use Spark for parallel jobs\n",
" \"force_cancel\": True, # Force cancel jobs if time limit is reached\n",
"}\n",
"\n",
"automl.fit(\n",
" dataframe=psdf,\n",
" label=label,\n",
" **automl_settings\n",
")\n",
"```\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33mragproxyagent\u001b[0m (to assistant):\n",
"\n",
"\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33massistant\u001b[0m (to ragproxyagent):\n",
"\n",
"UPDATE CONTEXT\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[32mUpdating context and resetting conversation.\u001b[0m\n",
"VectorDB returns doc_ids: [['bdfbc921', '7968cf3c']]\n",
"\u001b[32mNo more context, will terminate.\u001b[0m\n",
"\u001b[33mragproxyagent\u001b[0m (to assistant):\n",
"\n",
"TERMINATE\n",
"\n",
"--------------------------------------------------------------------------------\n"
]
}
],
"source": [
"# reset the assistant. Always reset the assistant before starting a new conversation.\n",
"assistant.reset()\n",
"\n",
"# given a problem, we use the ragproxyagent to generate a prompt to be sent to the assistant as the initial message.\n",
"# the assistant receives the message and generates a response. The response will be sent back to the ragproxyagent for processing.\n",
"# The conversation continues until the termination condition is met, in RetrieveChat, the termination condition when no human-in-loop is no code block detected.\n",
"# With human-in-loop, the conversation will continue until the user says \"exit\".\n",
"code_problem = \"How can I use FLAML to perform a classification task and use spark to do parallel training. Train 30 seconds and force cancel jobs if time limit is reached.\"\n",
"chat_result = ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=code_problem)"
]
}
],
"metadata": {
"front_matter": {
"description": "Explore the use of AutoGen's RetrieveChat for tasks like code generation from docstrings, answering complex questions with human feedback, and exploiting features like Update Context, custom prompts, and few-shot learning.",
"tags": [
"RAG"
]
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
},
"skip_test": "Requires interactive usage"
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,304 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Redact Sensitive Information Post-Response with Hooks\n",
"\n",
"This notebook shows an example on how we can use hooks in agents to effectively hide/redact sensitive data (i.e. API keys) from getting printed out to avoid potential data leak from agent's responses.\n",
"\n",
"## What are Hooks in Autogen?\n",
"\n",
"Similar to hooks in other frameworks, Autogen hooks allow you to customize your agent at each of its response-generation stages. \n",
"\n",
"Autogen offers 3 hookable methods: \n",
"\n",
"`process_last_received_message`:Process the last received message, invoked right before a new message is delivered to an agent. \n",
"`process_all_messages_before_reply`: Process incoming messages to an agent, invoked before an agent start generating a response. \n",
"`process_message_before_send`: Process the agent's generated response, invoked after response generation. \n",
"\n",
"To use them, simply pass a hookable method as a string in hookable_method attribute and your own hook function in hook attribute in register_hook. For example: \n",
"\n",
"`my_agent.register_hook(hookable_method=\"process_message_before_send\", hook=my_hook_function)`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Requirements\n",
"\n",
"AutoGen requires `Python>=3.8`. To run this notebook example, please install autogen-agentchat:\n",
"\n",
"````{=mdx}\n",
":::info Requirements\n",
"Install `pyautogen`:\n",
"```bash\n",
"pip install autogen-agentchat~=0.2\n",
"```\n",
"\n",
"For more information, please refer to the [installation guide](/docs/installation/).\n",
":::\n",
"````"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"import copy\n",
"import os\n",
"from typing import Dict, Optional, Union\n",
"\n",
"import autogen\n",
"from autogen import Agent, ConversableAgent\n",
"from autogen.code_utils import create_virtual_env\n",
"from autogen.coding import LocalCommandLineCodeExecutor"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Set Environment Variables"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"VENV_DIR\"] = \"<Your venv directory that is used for code executor agent>\"\n",
"os.environ[\"TEMP_FILE_DIR\"] = \"<Your directory for temporary code files>\"\n",
"os.environ[\"LLM_API_KEY\"] = \"<Your API key for llm config>\"\n",
"os.environ[\"LLM_API_TYPE\"] = \"<Your API type for llm config>\"\n",
"os.environ[\"LLM_API_VERSION\"] = \"<Your API version for llm config>\"\n",
"os.environ[\"LLM_API_URL\"] = \"<Your API Url for llm config>\"\n",
"\n",
"# Get the env variables\n",
"venv_dir = os.environ[\"VENV_DIR\"]\n",
"temp_file_fir = os.environ[\"TEMP_FILE_DIR\"]\n",
"\n",
"llm_config = {\n",
" \"config_list\": [\n",
" {\n",
" \"model\": \"gpt-4\",\n",
" \"api_key\": os.environ[\"LLM_API_KEY\"],\n",
" \"api_type\": os.environ[\"LLM_API_TYPE\"],\n",
" \"api_version\": os.environ[\"LLM_API_VERSION\"],\n",
" \"base_url\": os.environ[\"LLM_API_URL\"],\n",
" }\n",
" ],\n",
"}\n",
"\n",
"# The string that sensitive data will be redated to\n",
"replacementString = \"REDACTED\"\n",
"\n",
"# Get the env secrets and setup virtual env for agent to use\n",
"env_secrets = os.environ\n",
"\n",
"venv_context = create_virtual_env(venv_dir)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Setup the Agents\n",
"\n",
"We have 2 agents here: A proxy agent and a code executor agent. The user communicates to the code executer agent through the proxy agent."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"executor = LocalCommandLineCodeExecutor(timeout=10, work_dir=temp_file_fir, virtual_env_context=venv_context)\n",
"\n",
"code_executor_agent = ConversableAgent(\n",
" \"code_executor_agent\",\n",
" llm_config=False,\n",
" code_execution_config={\"executor\": executor},\n",
" human_input_mode=\"NEVER\",\n",
" max_consecutive_auto_reply=1,\n",
")\n",
"\n",
"proxy_agent = autogen.UserProxyAgent(\n",
" \"user_proxy\",\n",
" human_input_mode=\"NEVER\",\n",
" llm_config=llm_config,\n",
" system_message=\"DO NOT display any thing that is sensitive\",\n",
" max_consecutive_auto_reply=1,\n",
" code_execution_config={\"use_docker\": False},\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The function `transform_generated_response` will redact the message and will be registered as one of the agent's hooks. It will be invoked after the agent generates a response but before the agent sends out the response to the next agent. \n",
"\n",
"`transform_generated_response` requires the hook to accept one argument `message` that can be either a dictionary or a string. The argument `message` is the agent's response and sensitive information will be redacted from it. There're 3 other inputs `sender` `recipient` and `silent` but those are not required for message processing."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"def transform_generated_response(\n",
" message: Union[Dict, str], sender: Optional[Agent] = None, recipient: Agent = None, silent: bool = None\n",
") -> Union[Dict, str]:\n",
" temp_message = copy.deepcopy(message)\n",
" all_secrets = sorted(env_secrets.values(), key=len, reverse=True)\n",
" if isinstance(temp_message, Dict):\n",
" for secret in all_secrets:\n",
" if isinstance(temp_message[\"content\"], str):\n",
" if secret != \"\" and secret in temp_message[\"content\"]:\n",
" temp_message[\"content\"] = temp_message[\"content\"].replace(secret, replacementString)\n",
" elif isinstance(temp_message[\"content\"], list):\n",
" for item in temp_message[\"content\"]:\n",
" if item[\"type\"] == \"text\":\n",
" if secret != \"\" and secret in item[\"text\"]:\n",
" item[\"text\"] = item[\"text\"].replace(secret, replacementString)\n",
" if isinstance(temp_message, str):\n",
" for secret in all_secrets:\n",
" if secret != \"\" and secret in temp_message:\n",
" temp_message = temp_message.replace(secret, replacementString)\n",
"\n",
" return temp_message"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Use the `register_hook` method to register `transform_generated_response` to our code executor agent.\n",
"\n",
"We pass `\"process_message_before_send\"` here so that our message transformation function can be executed after the response is generated and before it's sent out."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"code_executor_agent.register_hook(hookable_method=\"process_message_before_send\", hook=transform_generated_response)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Initiate a Chat and Print the Result\n",
"\n",
"Initiate a chat by asking our proxy agent to execute a piece of code that retrieves an API key from the environment. The code executor agent will then execute the code but the response will be redacted to hide the API key."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"agent_message = \"\"\"Run the code and show me the printed variable.\n",
"The code block is below:\n",
"```python\n",
"import os\n",
"print(os.environ[\"LLM_API_KEY\"])\n",
"```\n",
"This is the end of the message.\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33muser_proxy\u001b[0m (to code_executor_agent):\n",
"\n",
"Run the code and show me the printed variable.\n",
"The code block is below:\n",
"```python\n",
"import os\n",
"print(os.environ[\"LLM_API_KEY\"])\n",
"```\n",
"This is the end of the message.\n",
"\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[31m\n",
">>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...\u001b[0m\n",
"\u001b[33mcode_executor_agent\u001b[0m (to user_proxy):\n",
"\n",
"exitcode: REDACTED (executiREDACTED succeeded)\n",
"Code output: REDACTED\n",
"\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33muser_proxy\u001b[0m (to code_executor_agent):\n",
"\n",
"Sorry, but I can't assist with that.\n",
"\n",
"--------------------------------------------------------------------------------\n"
]
}
],
"source": [
"result = proxy_agent.initiate_chat(code_executor_agent, message=agent_message, clear_history=False)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ChatResult(chat_id=None, chat_history=[{'content': 'Run the code and show me the printed variable.\\nThe code block is below:\\n```python\\nimport os\\nprint(os.environ[\"LLM_API_KEY\"])\\n```\\nThis is the end of the message.\\n', 'role': 'assistant'}, {'content': 'exitcode: REDACTED (executiREDACTED succeeded)\\nCode output: REDACTED\\n', 'role': 'user'}, {'content': \"Sorry, but I can't assist with that.\", 'role': 'assistant'}], summary=\"Sorry, but I can't assist with that.\", cost={'usage_including_cached_inference': {'total_cost': 0.00318, 'gpt-4': {'cost': 0.00318, 'prompt_tokens': 86, 'completion_tokens': 10, 'total_tokens': 96}}, 'usage_excluding_cached_inference': {'total_cost': 0}}, human_input=[])\n"
]
}
],
"source": [
"print(result)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -107,6 +107,7 @@ extra_require = {
"cohere": ["cohere>=5.5.8"],
"ollama": ["ollama>=0.3.3", "fix_busted_json>=0.0.18"],
"bedrock": ["boto3>=1.34.149"],
"kubernetes": ["kubernetes>=27.2.0"],
}
setuptools.setup(

View File

@ -0,0 +1,44 @@
# Test Environment for autogen.coding.kubernetes.PodCommandLineCodeExecutor
To test PodCommandLineCodeExecutor, the following environment is required.
- kubernetes cluster config file
- autogen package
## kubernetes cluster config file
kubernetes cluster config file, kubeconfig file's location should be set on environment variable `KUBECONFIG` or
It must be located in the .kube/config path of your home directory.
For Windows, `C:\Users\<<user>>\.kube\config`,
For Linux or MacOS, place the kubeconfig file in the `/home/<<user>>/.kube/config` directory.
## package install
Clone autogen github repository for package install and testing
Clone the repository with the command below.
before contribution
```sh
git clone -b k8s-code-executor https://github.com/questcollector/autogen.git
```
after contribution
```sh
git clone https://github.com/microsoft/autogen.git
```
install autogen with kubernetes >= 27.0.2
```sh
cd autogen
pip install .[kubernetes] -U
```
## test execution
Perform the test with the following command
```sh
pytest test/coding/test_kubernetes_commandline_code_executor.py
```

View File

@ -0,0 +1,203 @@
import importlib
import os
import sys
from pathlib import Path
import pytest
from autogen.code_utils import TIMEOUT_MSG
from autogen.coding.base import CodeBlock, CodeExecutor
try:
from autogen.coding.kubernetes import PodCommandLineCodeExecutor
client = importlib.import_module("kubernetes.client")
config = importlib.import_module("kubernetes.config")
kubeconfig = Path(".kube/config")
if os.environ.get("KUBECONFIG", None):
kubeconfig = Path(os.environ["KUBECONFIG"])
elif sys.platform == "win32":
kubeconfig = os.environ["userprofile"] / kubeconfig
else:
kubeconfig = os.environ["HOME"] / kubeconfig
if kubeconfig.is_file():
config.load_config(config_file=str(kubeconfig))
api_client = client.CoreV1Api()
api_client.list_namespace()
skip_kubernetes_tests = False
else:
skip_kubernetes_tests = True
pod_spec = client.V1Pod(
metadata=client.V1ObjectMeta(
name="abcd", namespace="default", annotations={"sidecar.istio.io/inject": "false"}
),
spec=client.V1PodSpec(
restart_policy="Never",
containers=[
client.V1Container(
args=["-c", "while true;do sleep 5; done"],
command=["/bin/sh"],
name="abcd",
image="python:3.11-slim",
env=[
client.V1EnvVar(name="TEST", value="TEST"),
client.V1EnvVar(
name="POD_NAME",
value_from=client.V1EnvVarSource(
field_ref=client.V1ObjectFieldSelector(field_path="metadata.name")
),
),
],
)
],
),
)
except Exception:
skip_kubernetes_tests = True
@pytest.mark.skipif(skip_kubernetes_tests, reason="kubernetes not accessible")
def test_create_default_pod_executor():
with PodCommandLineCodeExecutor(namespace="default", kube_config_file=str(kubeconfig)) as executor:
assert executor.timeout == 60
assert executor.work_dir == Path("/workspace")
assert executor._container_name == "autogen-code-exec"
assert executor._pod.metadata.name.startswith("autogen-code-exec-")
_test_execute_code(executor)
@pytest.mark.skipif(skip_kubernetes_tests, reason="kubernetes not accessible")
def test_create_node_pod_executor():
with PodCommandLineCodeExecutor(
image="node:22-alpine",
namespace="default",
work_dir="./app",
timeout=30,
kube_config_file=str(kubeconfig),
execution_policies={"javascript": True},
) as executor:
assert executor.timeout == 30
assert executor.work_dir == Path("./app")
assert executor._container_name == "autogen-code-exec"
assert executor._pod.metadata.name.startswith("autogen-code-exec-")
assert executor.execution_policies["javascript"]
# Test single code block.
code_blocks = [CodeBlock(code="console.log('hello world!')", language="javascript")]
code_result = executor.execute_code_blocks(code_blocks)
assert code_result.exit_code == 0 and "hello world!" in code_result.output and code_result.code_file is not None
# Test multiple code blocks.
code_blocks = [
CodeBlock(code="console.log('hello world!')", language="javascript"),
CodeBlock(code="let a = 100 + 100; console.log(a)", language="javascript"),
]
code_result = executor.execute_code_blocks(code_blocks)
assert (
code_result.exit_code == 0
and "hello world!" in code_result.output
and "200" in code_result.output
and code_result.code_file is not None
)
# Test running code.
file_lines = ["console.log('hello world!')", "let a = 100 + 100", "console.log(a)"]
code_blocks = [CodeBlock(code="\n".join(file_lines), language="javascript")]
code_result = executor.execute_code_blocks(code_blocks)
assert (
code_result.exit_code == 0
and "hello world!" in code_result.output
and "200" in code_result.output
and code_result.code_file is not None
)
@pytest.mark.skipif(skip_kubernetes_tests, reason="kubernetes not accessible")
def test_create_pod_spec_pod_executor():
with PodCommandLineCodeExecutor(
pod_spec=pod_spec, container_name="abcd", kube_config_file=str(kubeconfig)
) as executor:
assert executor.timeout == 60
assert executor._container_name == "abcd"
assert executor._pod.metadata.name == pod_spec.metadata.name
assert executor._pod.metadata.namespace == pod_spec.metadata.namespace
_test_execute_code(executor)
# Test bash script.
if sys.platform not in ["win32"]:
code_blocks = [CodeBlock(code="echo $TEST $POD_NAME", language="bash")]
code_result = executor.execute_code_blocks(code_blocks)
assert (
code_result.exit_code == 0 and "TEST abcd" in code_result.output and code_result.code_file is not None
)
@pytest.mark.skipif(skip_kubernetes_tests, reason="kubernetes not accessible")
def test_pod_executor_timeout():
with PodCommandLineCodeExecutor(namespace="default", timeout=5, kube_config_file=str(kubeconfig)) as executor:
assert executor.timeout == 5
assert executor.work_dir == Path("/workspace")
assert executor._container_name == "autogen-code-exec"
assert executor._pod.metadata.name.startswith("autogen-code-exec-")
# Test running code.
file_lines = ["import time", "time.sleep(10)", "a = 100 + 100", "print(a)"]
code_blocks = [CodeBlock(code="\n".join(file_lines), language="python")]
code_result = executor.execute_code_blocks(code_blocks)
assert code_result.exit_code == 124 and TIMEOUT_MSG in code_result.output and code_result.code_file is not None
def _test_execute_code(executor: CodeExecutor) -> None:
# Test single code block.
code_blocks = [CodeBlock(code="import sys; print('hello world!')", language="python")]
code_result = executor.execute_code_blocks(code_blocks)
assert code_result.exit_code == 0 and "hello world!" in code_result.output and code_result.code_file is not None
# Test multiple code blocks.
code_blocks = [
CodeBlock(code="import sys; print('hello world!')", language="python"),
CodeBlock(code="a = 100 + 100; print(a)", language="python"),
]
code_result = executor.execute_code_blocks(code_blocks)
assert (
code_result.exit_code == 0
and "hello world!" in code_result.output
and "200" in code_result.output
and code_result.code_file is not None
)
# Test bash script.
if sys.platform not in ["win32"]:
code_blocks = [CodeBlock(code="echo 'hello world!'", language="bash")]
code_result = executor.execute_code_blocks(code_blocks)
assert code_result.exit_code == 0 and "hello world!" in code_result.output and code_result.code_file is not None
# Test running code.
file_lines = ["import sys", "print('hello world!')", "a = 100 + 100", "print(a)"]
code_blocks = [CodeBlock(code="\n".join(file_lines), language="python")]
code_result = executor.execute_code_blocks(code_blocks)
assert (
code_result.exit_code == 0
and "hello world!" in code_result.output
and "200" in code_result.output
and code_result.code_file is not None
)
# Test running code has filename.
file_lines = ["# filename: test.py", "import sys", "print('hello world!')", "a = 100 + 100", "print(a)"]
code_blocks = [CodeBlock(code="\n".join(file_lines), language="python")]
code_result = executor.execute_code_blocks(code_blocks)
print(code_result.code_file)
assert (
code_result.exit_code == 0
and "hello world!" in code_result.output
and "200" in code_result.output
and code_result.code_file.find("test.py") > 0
)
# Test error code.
code_blocks = [CodeBlock(code="print(sys.platform)", language="python")]
code_result = executor.execute_code_blocks(code_blocks)
assert code_result.exit_code == 1 and "Traceback" in code_result.output and code_result.code_file is not None

View File

@ -406,3 +406,4 @@ You can check out more example notebooks for RAG use cases:
- [Using RetrieveChat with Qdrant for Retrieve Augmented Code Generation and Question Answering](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_qdrant.ipynb)
- [Using RetrieveChat Powered by PGVector for Retrieve Augmented Code Generation and Question Answering](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_pgvector.ipynb)
- [Using RetrieveChat Powered by MongoDB Atlas for Retrieve Augmented Code Generation and Question Answering](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_mongodb.ipynb)
- [Using RetrieveChat Powered by Couchbase for Retrieve Augmented Code Generation and Question Answering](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_couchbase.ipynb)

View File

@ -0,0 +1,102 @@
# Agent Memory with Zep
<img src="https://raw.githubusercontent.com/getzep/zep/refs/heads/main/assets/zep-logo-icon-gradient-rgb.svg?raw=true" style="width: 20%;" alt="Zep logo"/>
[Zep](https://www.getzep.com/?utm_source=autogen) is a long-term memory service for agentic applications used by both startups and enterprises. With Zep, you can build personalized, accurate, and production-ready agent applications.
Zep's memory continuously learns facts from interactions with users and your changing business data. With [just two API calls](https://help.getzep.com/memory?utm_source=autogen), you can persist chat history to Zep and recall facts relevant to the state of your agent.
Zep is powered by a temporal Knowledge Graph that allows reasoning with facts as they change. A combination of semantic and graph search enables accurate and low-latency fact retrieval.
Sign up for [Zep Cloud](https://www.getzep.com/?utm_source=autogen) or visit the [Zep Community Edition Repo](https://github.com/getzep/zep).
| Feature | Description |
| ---------------------------------------------- | ------------------------------------------------------------------------------------- |
| 💬 **Capture Detailed Conversational Context** | Zep's Knowledge Graph-based memory captures episodic, semantic, and temporal contexts |
| 🗄️ **Business Data is Context, too** | Zep is able to extract facts from JSON and unstructured text as well |
| ⚙️ **Tailor For Your Business** | Fact Ratings and other tools allow you to fine-tune retrieval for your use case |
| ⚡️ **Instant Memory Retrieval** | Retrieve relevant facts in under 100ms |
| 🔐 **Compliance & Security** | User Privacy Management, SOC 2 Type II certification, and other controls |
| 🖼️ **Framework Agnostic & Future-Proof** | Use with AutoGen or any other framework, current or future |
<details>
<summary><b><u>Zep Community Edition Walkthrough</u></b></summary>
<a href="https://vimeo.com/1013045013">
<img src="img/ecosystem-zep-ce-walkthrough.png" alt="Zep Fact Ratings" />
</a>
</details>
<details open>
<summary><b><u>User Chat Session and Facts</u></b></summary>
<a href="https://help.getzep.com/chat-history-memory/facts?utm_source=autogen">
<img src="img/ecosystem-zep-session.gif" style="width: 100%;" alt="Chat Session and Facts"/>
</a>
</details>
<details>
<summary><b><u>Implementing Fact Ratings</u></b></summary>
<a href="https://vimeo.com/989192145">
<img src="img/ecosystem-zep-fact-ratings.png" alt="Zep Fact Ratings" />
</a>
</details>
## How Zep works
1. Add chat messages or data artifacts to Zep during each user interaction or agent event.
2. Zep intelligently integrates new information into the user's (or groups of users) Knowledge Graph, updating existing context as needed.
3. Retrieve relevant facts from Zep for subsequent interactions or events.
Zep's temporal Knowledge Graph maintains contextual information about facts, enabling reasoning about state changes and providing data provenance insights. Each fact includes `valid_at` and `invalid_at` dates, allowing agents to track changes in user preferences, traits, or environment.
## Zep is fast
Retrieving facts is simple and very fast. Unlike other memory solutions, Zep does not use agents to ensure facts are relevant. It precomputes facts, entity summaries, and other artifacts asynchronously. For on-premise use, retrieval speed primarily depends on your embedding service's performance.
## Zep supports many types of data
You can add a variety of data artifacts to Zep:
- Adding chat history messages.
- Ingestion of JSON and unstructured text.
Zep supports chat session, user, and group-level graphs. Group graphs allow for capturing organizational knowledge.
## Getting Started
### Zep Cloud
1. Sign up for [Zep Cloud](https://www.getzep.com?utm_source=autogen) and create a [Project API Key](https://help.getzep.com/projects?utm_source=autogen).
2. Install one of the [Zep Python, TypeScript or Go SDKs](https://help.getzep.com/sdks?utm_source=autogen). Python instructions shown below.
```shell
pip install zep-cloud
```
3. Initialize a client
```python
import os
from zep_cloud.client import AsyncZep
API_KEY = os.environ.get('ZEP_API_KEY')
client = AsyncZep(
api_key=API_KEY,
)
```
3. Review the Zep and Autogen [notebook example](/docs/notebooks/agent_memory_using_zep/) for agent-building best practices.
### Zep Community Edition
Follow the [Getting Started guide](https://help.getzep.com/ce/quickstart?utm_source=autogen) or visit the [GitHub Repo](https://github.com/getzep/zep?utm_source=autogen).
## Autogen + Zep examples
- [Autogen Agents with Zep Memory Notebook](/docs/notebooks/agent_memory_using_zep/)
## Extra links
- [📙 Documentation](https://help.getzep.com/?utm_source=autogen)
- [🐦 Twitter / X](https://x.com/zep_ai/)
- [📢 Discord](https://discord.com/invite/W8Kw6bsgXQ)

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c0829b29a48ca05e2694aca00446ef5768c1b8edec56ce5035527f25f9ee4c81
size 421633

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:179241bd4fa3ed89d721deeb1810a31b9838e7f54582d521bd91f29cbae044f2
size 233905

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

View File

@ -0,0 +1,773 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kubernetes Pod Commandline Code Executor"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `PodCommandLineCodeExecutor` in the `autogen.coding.kubernetes` module is designed to execute code blocks using a pod in Kubernetes.\n",
"It functions similarly to the `DockerCommandLineCodeExecutor`, but specifically creates container within Kubernetes environments.\n",
"\n",
"There are two condition to use PodCommandLineCodeExecutor.\n",
"- Access to a Kubernetes cluster\n",
"- installation `autogen` with the extra requirements `'pyautogen[kubernetes]'`\n",
"\n",
"For local development and testing, this document uses a Minikube cluster.\n",
"\n",
"Minikube is a tool that allows you to run a single-node Kubernetes cluster on you local machine. \n",
"You can refer to the link below for installation and setup of Minikube.\n",
"\n",
"🔗 https://minikube.sigs.k8s.io/docs/start/"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Access kubernetes cluster"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are four options PodCommandLineCodeExecutor to access kubernetes API server.\n",
"- default kubeconfig file path: `~/.kube/config`\n",
"- Provide a custom kubeconfig file path using the `kube_config_file` argument of `PodCommandLineCodeExecutor`.\n",
"- Set the kubeconfig file path using the `KUBECONFIG` environment variable.\n",
"- Provide token from Kubernetes ServiceAccount with sufficient permissions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Generally, if kubeconfig file is located in `~/.kube/config`, there's no need to provide kubeconfig file path on parameter or environment variables.\n",
"\n",
"The tutorial of providing ServiceAccount Token is in the last section"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example\n",
"\n",
"In order to use kubernetes Pod based code executor, you need to install Kubernetes Python SDK.\n",
"\n",
"You can do this by running the following command:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pip install 'kubernetes>=27'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively, you can install it with the extra features for Kubernetes:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pip install 'autogen-agentchat[kubernetes]~=0.2'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To provide kubeconfig file path with environment variable, It can be added with `os.environ[\"KUBECONFIG\"]`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"# Set the KUBECONFIG environment variable\n",
"# if the kubeconfig file is not in the default location(~/.kube/config).\n",
"os.environ[\"KUBECONFIG\"] = \"path/to/your/kubeconfig\""
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from autogen.coding import CodeBlock\n",
"from autogen.coding.kubernetes import PodCommandLineCodeExecutor"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"exit_code=0 output='Hello, World!\\n' code_file='/workspace/tmp_code_07da107bb575cc4e02b0e1d6d99cc204.py'\n"
]
}
],
"source": [
"with PodCommandLineCodeExecutor(\n",
" namespace=\"default\",\n",
" # kube_config_file=\"kubeconfig/file/path\" # If you have another kubeconfig file, you can add it on kube_config_file argument\n",
") as executor:\n",
" print(\n",
" executor.execute_code_blocks(\n",
" # Example of executing a simple Python code block within a Kubernetes pod.\n",
" code_blocks=[\n",
" CodeBlock(language=\"python\", code=\"print('Hello, World!')\"),\n",
" ]\n",
" )\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using a context manager(the `with` statement), the pod created by `PodCommandLineCodeExecutor` is automatically deleted after the tasks are completed.\n",
"\n",
"Although the pod is automatically deleted when using a context manager, you might sometimes need to delete it manually. You can do this using `stop()` method, as shown below:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"executor = PodCommandLineCodeExecutor(namespace=\"default\")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"NAME READY STATUS RESTARTS AGE\n",
"autogen-code-exec-afd217ac-f77b-4ede-8c53-1297eca5ec64 1/1 Running 0 10m\n"
]
}
],
"source": [
"%%bash\n",
"# This command lists all pods in the default namespace. \n",
"# The default pod name follows the format autogen-code-exec-{uuid.uuid4()}.\n",
"kubectl get pod -n default"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"python:3-slim"
]
}
],
"source": [
"%%bash\n",
"# This command shows container's image in the pod.\n",
"# The default container image is python:3-slim\n",
"kubectl get pod autogen-code-exec-afd217ac-f77b-4ede-8c53-1297eca5ec64 -o jsonpath={.spec.containers[0].image}"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"executor.stop()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To use a different container image for code executor pod, specify the desired image tag using `image` argument.\n",
"\n",
"`PodCommandLineCodeExecutor` has a default execution policy that allows Python and shell script code blocks. You can enable other languages with `execution_policies` argument."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"exit_code=0 output='Hello, World!\\n' code_file='app/tmp_code_8c34c8586cb47943728afe1297b7a51c.js'\n"
]
}
],
"source": [
"with PodCommandLineCodeExecutor(\n",
" image=\"node:22-alpine\", # Specifies the runtime environments using a container image\n",
" namespace=\"default\",\n",
" work_dir=\"./app\", # Directory within the container where code block files are stored\n",
" timeout=10, # Timeout in seconds for pod creation and code block execution (default is 60 seconds)\n",
" execution_policies={\n",
" \"javascript\": True\n",
" }, # Enable execution of Javascript code blocks by updating execution policies\n",
") as executor:\n",
" print(\n",
" executor.execute_code_blocks(\n",
" code_blocks=[\n",
" CodeBlock(language=\"javascript\", code=\"console.log('Hello, World!')\"),\n",
" ]\n",
" )\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want to apply custom settings for executor pod, such as annotations, environment variables, commands, volumes etc., \n",
"you can provide a custom pod specification using `kubernetes.client.V1Pod` format.\n",
"\n",
"The `container_name` argument should also be provided because `PodCommandLineCodeExecutor` does not automatically recognize the container where code blocks will be executed."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"from kubernetes import client\n",
"\n",
"pod = client.V1Pod(\n",
" metadata=client.V1ObjectMeta(name=\"abcd\", namespace=\"default\", annotations={\"sidecar.istio.io/inject\": \"false\"}),\n",
" spec=client.V1PodSpec(\n",
" restart_policy=\"Never\",\n",
" containers=[\n",
" client.V1Container(\n",
" args=[\"-c\", \"while true;do sleep 5; done\"],\n",
" command=[\"/bin/sh\"],\n",
" name=\"abcd\", # container name where code blocks will be executed should be provided using `container_name` argument\n",
" image=\"python:3.11-slim\",\n",
" env=[\n",
" client.V1EnvVar(name=\"TEST\", value=\"TEST\"),\n",
" client.V1EnvVar(\n",
" name=\"POD_NAME\",\n",
" value_from=client.V1EnvVarSource(\n",
" field_ref=client.V1ObjectFieldSelector(field_path=\"metadata.name\")\n",
" ),\n",
" ),\n",
" ],\n",
" )\n",
" ],\n",
" ),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"exit_code=0 output='Hello, World!\\n' code_file='/autogen/tmp_code_07da107bb575cc4e02b0e1d6d99cc204.py'\n",
"exit_code=0 output='TEST abcd\\n' code_file='/autogen/tmp_code_202399627ea7fb8d8e816f4910b7f87b.sh'\n"
]
}
],
"source": [
"with PodCommandLineCodeExecutor(\n",
" pod_spec=pod, # custom executor pod spec\n",
" container_name=\"abcd\", # To use custom executor pod spec, container_name where code block will be executed should be specified\n",
" work_dir=\"/autogen\",\n",
" timeout=60,\n",
") as executor:\n",
" print(\n",
" executor.execute_code_blocks(\n",
" code_blocks=[\n",
" CodeBlock(language=\"python\", code=\"print('Hello, World!')\"),\n",
" ]\n",
" )\n",
" )\n",
" print(\n",
" executor.execute_code_blocks(\n",
" code_blocks=[\n",
" CodeBlock(\n",
" code=\"echo $TEST $POD_NAME\", language=\"bash\"\n",
" ), # echo environment variables specified in pod_spec\n",
" ]\n",
" )\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Integrates with AutoGen Agents"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`PodCommandLineCodeExecutor` can be integrated with Agents."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"from autogen import config_list_from_json\n",
"\n",
"config_list = config_list_from_json(\n",
" env_or_file=\"OAI_CONFIG_LIST\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mcode_executor_agent\u001b[0m (to code_writer):\n",
"\n",
"Write Python code to calculate the moves of disk on tower of hanoi with 3 disks\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33mcode_writer\u001b[0m (to code_executor_agent):\n",
"\n",
"The problem of the Tower of Hanoi with 3 disks involves moving the disks from one peg to another, following these rules:\n",
"1. Only one disk can be moved at a time.\n",
"2. Each move consists of taking the upper disk from one of the stacks and placing it on top of another stack or on an empty peg.\n",
"3. No disk may be placed on top of a smaller disk.\n",
"\n",
"In the solution, I will use a recursive function to calculate the moves and print them out. Here's the Python code to accomplish this:\n",
"\n",
"```python\n",
"def tower_of_hanoi(n, from_rod, to_rod, aux_rod):\n",
" if n == 1:\n",
" print(f\"Move disk 1 from rod {from_rod} to rod {to_rod}\")\n",
" return\n",
" tower_of_hanoi(n-1, from_rod, aux_rod, to_rod)\n",
" print(f\"Move disk {n} from rod {from_rod} to rod {to_rod}\")\n",
" tower_of_hanoi(n-1, aux_rod, to_rod, from_rod)\n",
"\n",
"n = 3 # Number of disks\n",
"tower_of_hanoi(n, 'A', 'C', 'B') # A, B and C are names of the rods\n",
"```\n",
"\n",
"This script defines a function `tower_of_hanoi` that will print out each move necessary to solve the Tower of Hanoi problem with the specified number of disks `n`. This specific setup will solve for 3 disks moving from rod 'A' to rod 'C' with the help of rod 'B'.\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[31m\n",
">>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...\u001b[0m\n",
"\u001b[33mcode_executor_agent\u001b[0m (to code_writer):\n",
"\n",
"exitcode: 0 (execution succeeded)\n",
"Code output: Move disk 1 from rod A to rod C\n",
"Move disk 2 from rod A to rod B\n",
"Move disk 1 from rod C to rod B\n",
"Move disk 3 from rod A to rod C\n",
"Move disk 1 from rod B to rod A\n",
"Move disk 2 from rod B to rod C\n",
"Move disk 1 from rod A to rod C\n",
"\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33mcode_writer\u001b[0m (to code_executor_agent):\n",
"\n",
"The execution of the provided code successfully calculated and printed the moves for solving the Tower of Hanoi with 3 disks. Here are the steps it performed:\n",
"\n",
"1. Move disk 1 from rod A to rod C.\n",
"2. Move disk 2 from rod A to rod B.\n",
"3. Move disk 1 from rod C to rod B.\n",
"4. Move disk 3 from rod A to rod C.\n",
"5. Move disk 1 from rod B to rod A.\n",
"6. Move disk 2 from rod B to rod C.\n",
"7. Move disk 1 from rod A to rod C.\n",
"\n",
"This sequence effectively transfers all disks from rod A to rod C using rod B as an auxiliary, following the rules of the Tower of Hanoi puzzle. If you have any more tasks or need further explanation, feel free to ask!\n",
"\n",
"--------------------------------------------------------------------------------\n",
"\u001b[33mcode_executor_agent\u001b[0m (to code_writer):\n",
"\n",
"\n",
"\n",
"--------------------------------------------------------------------------------\n"
]
}
],
"source": [
"from autogen import ConversableAgent\n",
"\n",
"# The code writer agent's system message is to instruct the LLM on how to\n",
"# use the code executor with python or shell script code\n",
"code_writer_system_message = \"\"\"\n",
"You have been given coding capability to solve tasks using Python code.\n",
"In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute.\n",
" 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself.\n",
" 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly.\n",
"Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill.\n",
"When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user.\n",
"If you want the user to save the code in a file before executing it, put # filename: <filename> inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user.\n",
"\"\"\"\n",
"with PodCommandLineCodeExecutor(namespace=\"default\") as executor:\n",
"\n",
" code_executor_agent = ConversableAgent(\n",
" name=\"code_executor_agent\",\n",
" llm_config=False,\n",
" code_execution_config={\n",
" \"executor\": executor,\n",
" },\n",
" human_input_mode=\"NEVER\",\n",
" )\n",
"\n",
" code_writer_agent = ConversableAgent(\n",
" \"code_writer\",\n",
" system_message=code_writer_system_message,\n",
" llm_config={\"config_list\": config_list},\n",
" code_execution_config=False, # Turn off code execution for this agent.\n",
" max_consecutive_auto_reply=2,\n",
" human_input_mode=\"NEVER\",\n",
" )\n",
"\n",
" chat_result = code_executor_agent.initiate_chat(\n",
" code_writer_agent, message=\"Write Python code to calculate the moves of disk on tower of hanoi with 10 disks\"\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "93802984-3207-430b-a205-82f0a77df2b2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ChatResult(chat_id=None,\n",
" chat_history=[{'content': 'Write Python code to calculate the moves '\n",
" 'of disk on tower of hanoi with 3 disks',\n",
" 'name': 'code_executor_agent',\n",
" 'role': 'assistant'},\n",
" {'content': 'The problem of the Tower of Hanoi with 3 '\n",
" 'disks involves moving the disks from one '\n",
" 'peg to another, following these rules:\\n'\n",
" '1. Only one disk can be moved at a '\n",
" 'time.\\n'\n",
" '2. Each move consists of taking the '\n",
" 'upper disk from one of the stacks and '\n",
" 'placing it on top of another stack or on '\n",
" 'an empty peg.\\n'\n",
" '3. No disk may be placed on top of a '\n",
" 'smaller disk.\\n'\n",
" '\\n'\n",
" 'In the solution, I will use a recursive '\n",
" 'function to calculate the moves and '\n",
" \"print them out. Here's the Python code \"\n",
" 'to accomplish this:\\n'\n",
" '\\n'\n",
" '```python\\n'\n",
" 'def tower_of_hanoi(n, from_rod, to_rod, '\n",
" 'aux_rod):\\n'\n",
" ' if n == 1:\\n'\n",
" ' print(f\"Move disk 1 from rod '\n",
" '{from_rod} to rod {to_rod}\")\\n'\n",
" ' return\\n'\n",
" ' tower_of_hanoi(n-1, from_rod, '\n",
" 'aux_rod, to_rod)\\n'\n",
" ' print(f\"Move disk {n} from rod '\n",
" '{from_rod} to rod {to_rod}\")\\n'\n",
" ' tower_of_hanoi(n-1, aux_rod, to_rod, '\n",
" 'from_rod)\\n'\n",
" '\\n'\n",
" 'n = 3 # Number of disks\\n'\n",
" \"tower_of_hanoi(n, 'A', 'C', 'B') # A, B \"\n",
" 'and C are names of the rods\\n'\n",
" '```\\n'\n",
" '\\n'\n",
" 'This script defines a function '\n",
" '`tower_of_hanoi` that will print out '\n",
" 'each move necessary to solve the Tower '\n",
" 'of Hanoi problem with the specified '\n",
" 'number of disks `n`. This specific setup '\n",
" 'will solve for 3 disks moving from rod '\n",
" \"'A' to rod 'C' with the help of rod 'B'.\",\n",
" 'name': 'code_writer',\n",
" 'role': 'user'},\n",
" {'content': 'exitcode: 0 (execution succeeded)\\n'\n",
" 'Code output: Move disk 1 from rod A to '\n",
" 'rod C\\n'\n",
" 'Move disk 2 from rod A to rod B\\n'\n",
" 'Move disk 1 from rod C to rod B\\n'\n",
" 'Move disk 3 from rod A to rod C\\n'\n",
" 'Move disk 1 from rod B to rod A\\n'\n",
" 'Move disk 2 from rod B to rod C\\n'\n",
" 'Move disk 1 from rod A to rod C\\n',\n",
" 'name': 'code_executor_agent',\n",
" 'role': 'assistant'},\n",
" {'content': 'The execution of the provided code '\n",
" 'successfully calculated and printed the '\n",
" 'moves for solving the Tower of Hanoi '\n",
" 'with 3 disks. Here are the steps it '\n",
" 'performed:\\n'\n",
" '\\n'\n",
" '1. Move disk 1 from rod A to rod C.\\n'\n",
" '2. Move disk 2 from rod A to rod B.\\n'\n",
" '3. Move disk 1 from rod C to rod B.\\n'\n",
" '4. Move disk 3 from rod A to rod C.\\n'\n",
" '5. Move disk 1 from rod B to rod A.\\n'\n",
" '6. Move disk 2 from rod B to rod C.\\n'\n",
" '7. Move disk 1 from rod A to rod C.\\n'\n",
" '\\n'\n",
" 'This sequence effectively transfers all '\n",
" 'disks from rod A to rod C using rod B as '\n",
" 'an auxiliary, following the rules of the '\n",
" 'Tower of Hanoi puzzle. If you have any '\n",
" 'more tasks or need further explanation, '\n",
" 'feel free to ask!',\n",
" 'name': 'code_writer',\n",
" 'role': 'user'},\n",
" {'content': '',\n",
" 'name': 'code_executor_agent',\n",
" 'role': 'assistant'}],\n",
" summary='',\n",
" cost={'usage_excluding_cached_inference': {'total_cost': 0},\n",
" 'usage_including_cached_inference': {'gpt-4-turbo-2024-04-09': {'completion_tokens': 499,\n",
" 'cost': 0.0269,\n",
" 'prompt_tokens': 1193,\n",
" 'total_tokens': 1692},\n",
" 'total_cost': 0.0269}},\n",
" human_input=[])\n"
]
}
],
"source": [
"import pprint\n",
"\n",
"pprint.pprint(chat_result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Use ServiceAccount token"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If a `PodCommandLineCodeExecutor` instance runs inside of Kubernetes Pod, it can use a token generated from a ServiceAccount to access Kubernetes API server.\n",
"\n",
"The `PodCommandLineCodeExecutor` requires the following permissions:\n",
"the verbs `create`, `get`, `delete` for `pods` resource, and the verb `get` for resources `pods/status`, `pods/exec`.\n",
"\n",
"You can create a ServiceAccount, ClusterRole and RoleBinding with `kubectl` as shown below:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"serviceaccount/autogen-executor-sa created\n"
]
}
],
"source": [
"%%bash\n",
"# Create ServiceAccount on default namespace\n",
"kubectl create sa autogen-executor-sa"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"clusterrole.rbac.authorization.k8s.io/autogen-executor-role created\n"
]
}
],
"source": [
"%%bash\n",
"# Create ClusterRole that has sufficient permissions\n",
"kubectl create clusterrole autogen-executor-role \\\n",
" --verb=get,create,delete --resource=pods,pods/status,pods/exec"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"rolebinding.rbac.authorization.k8s.io/autogen-executor-rolebinding created\n"
]
}
],
"source": [
"%%bash\n",
"# Create RoleBinding that binds ClusterRole and ServiceAccount\n",
"kubectl create rolebinding autogen-executor-rolebinding \\\n",
" --clusterrole autogen-executor-role --serviceaccount default:autogen-executor-sa"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A pod with a previously created ServiceAccount can be launched using the following command."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"pod/autogen-executor created\n"
]
}
],
"source": [
"%%bash\n",
"# create pod with serviceaccount\n",
"kubectl run autogen-executor --image python:3 \\\n",
" --overrides='{\"spec\":{\"serviceAccount\": \"autogen-executor-sa\"}}' \\\n",
" -- bash -c 'pip install pyautogen[kubernetes] && sleep inifinity'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can execute `PodCommandLineCodeExecutor` inside the Python interpreter process from `autogen-executor` Pod.\n",
"\n",
"It creates new pod for code execution using token generated from `autogen-executor-sa` ServiceAccount."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%bash\n",
"kubectl exec autogen-executor -it -- python"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"kube_config_path not provided and default location (~/.kube/config) does not exist. Using inCluster Config. This might not work.\n",
"exit_code=0 output='Hello, World!\\n' code_file='/workspace/tmp_code_07da107bb575cc4e02b0e1d6d99cc204.py'"
]
}
],
"source": [
"from autogen.coding import CodeBlock\n",
"from autogen.coding.kubernetes import PodCommandLineCodeExecutor\n",
"\n",
"# PodCommandLineCodeExecutor uses token generated from ServiceAccount by kubernetes incluster config\n",
"with PodCommandLineCodeExecutor() as executor:\n",
" print(\n",
" executor.execute_code_blocks(\n",
" code_blocks=[\n",
" CodeBlock(language=\"python\", code=\"print('Hello, World!')\"),\n",
" ]\n",
" )\n",
" )"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "autogen",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -127,6 +127,7 @@ For more detailed examples and notebooks showcasing the usage of retrieval augme
- Automated Code Generation and Question Answering with [PGVector](https://github.com/pgvector/pgvector) based Retrieval Augmented Agents - [View Notebook](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_pgvector.ipynb)
- Automated Code Generation and Question Answering with [Qdrant](https://qdrant.tech/) based Retrieval Augmented Agents - [View Notebook](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_qdrant.ipynb)
- Automated Code Generation and Question Answering with [MongoDB Atlas](https://www.mongodb.com/) based Retrieval Augmented Agents - [View Notebook](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_mongodb.ipynb)
- Automated Code Generation and Question Answering with [Couchbase](https://www.couchbase.com/) based Retrieval Augmented Agents - [View Notebook](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_RetrieveChat_couchbase.ipynb)
- Chat with OpenAI Assistant with Retrieval Augmentation - [View Notebook](https://github.com/microsoft/autogen/blob/0.2/notebook/agentchat_oai_assistant_retrieval.ipynb)
- **RAG**: Group Chat with Retrieval Augmented Generation (with 5 group member agents and 1 manager agent) - [View Notebook](/docs/notebooks/agentchat_groupchat_RAG)

View File

@ -7,52 +7,55 @@ import styles from "./index.module.css";
import HomepageFeatures from "../components/HomepageFeatures";
function HomepageHeader() {
const { siteConfig } = useDocusaurusContext();
return (
<header className={clsx("hero hero--primary", styles.heroBanner)}>
<div className="container">
<h1 className="hero__title">{siteConfig.title}</h1>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className={styles.buttons}>
<div className={styles.buttonWrapper}>
<Link
className="button button--secondary button--lg"
to="/docs/Getting-Started"
>
Get Started
</Link>
<p className={styles.buttonTagline}>
Current stable version of AutoGen (autogen-agentchat~=0.2)
</p>
</div>
<div className={styles.buttonWrapper}>
<Link
className="button button--secondary button--lg"
to="https://microsoft.github.io/autogen/dev/"
>
Preview v0.4
</Link>
<p className={styles.buttonTagline}>
A new event driven architecture for AutoGen
</p>
</div>
</div>
</div>
</header>
);
const { siteConfig } = useDocusaurusContext();
return (
<header className={clsx("hero hero--primary", styles.heroBanner)}>
<div className="container">
<h1 className="hero__title">{siteConfig.title}</h1>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className={styles.buttons}>
<div className={styles.buttonWrapper}>
<Link
className="button button--secondary button--lg"
to="/docs/Getting-Started"
>
Get Started
</Link>
<p className={styles.buttonTagline}>
Current stable version of AutoGen (autogen-agentchat~=0.2)
</p>
</div>
<div className={styles.buttonWrapper}>
<Link
className={clsx(
"button button--secondary button--lg",
styles.buttonLink
)}
to="https://microsoft.github.io/autogen/dev/"
>
Preview v0.4
</Link>
<p className={styles.buttonTagline}>
A new event driven, asynchronous architecture for AutoGen
</p>
</div>
</div>
</div>
</header>
);
}
export default function Home() {
const { siteConfig } = useDocusaurusContext();
return (
<Layout
title={`AutoGen`}
description="Enabling Next-Gen LLM Applications via Multi-Agent Conversation Framework"
>
<HomepageHeader />
<main>
<HomepageFeatures />
</main>
</Layout>
);
const { siteConfig } = useDocusaurusContext();
return (
<Layout
title={`AutoGen`}
description="Enabling Next-Gen LLM Applications via Multi-Agent Conversation Framework"
>
<HomepageHeader />
<main>
<HomepageFeatures />
</main>
</Layout>
);
}

View File

@ -29,11 +29,18 @@
display: flex;
flex-direction: column;
align-items: center;
width: 200px;
}
.buttonLink {
width: 100%;
}
.buttonTagline {
font-size: 0.8rem;
margin-top: 5px;
text-align: center;
max-width: 200px;
height: 100px;
overflow-y: auto;
padding: 5px;
}