diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index f0e9767410..33eed8544d 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -1,10 +1,17 @@ -name: Deploy Redirect Page +name: Deploy MkDocs to GitHub Pages on: push: branches: [main] paths: + - "autogen/**" + - "website/**" - ".github/workflows/deploy-website.yml" + - ".github/workflows/docs-check-broken-links.yml" + - "scripts/broken-links-check.sh" + - "scripts/docs_build_mkdocs.sh" + - "scripts/docs_serve_mkdocs.sh" + - ".muffet-excluded-links.txt" workflow_dispatch: permissions: contents: write @@ -15,90 +22,34 @@ jobs: # Step 1: Check out the repository - name: Checkout repository uses: actions/checkout@v4 + with: + lfs: true + fetch-depth: 0 - # Step 2: Create redirect HTML files - - name: Create redirect HTML - run: | - mkdir -p dist - # Homepage redirect - cat > dist/index.html << 'EOF' - - - - - - - - - - Page Redirection - - - If you are not redirected automatically, follow this link to the new documentation. - - - EOF - - # Deep link handling - cat > dist/404.html << 'EOF' - - - - - - - - - Page Redirection - - - If you are not redirected automatically, follow this link to the new documentation. - - - EOF + # Step 3: Build MkDocs + - name: Build documentation + run: | + uv venv + . .venv/bin/activate + uv pip install -e ".[docs]" + ./scripts/docs_build_mkdocs.sh + ls -la ./website/mkdocs/site + working-directory: . # Step 3: Deploy to gh-pages branch - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist + publish_dir: ./website/mkdocs/site force_orphan: true user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com' - commit_message: 'Deploy redirect page' + commit_message: 'Deploy MkDocs site to GitHub pages' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 96dcee2bd4..ecddcd0021 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,6 +13,7 @@ repos: - id: check-added-large-files - id: check-ast - id: check-yaml + exclude: 'website/mkdocs/mkdocs.yml' - id: check-toml - id: check-json exclude: | diff --git a/.secrets.baseline b/.secrets.baseline index 7cb1418107..957ae6867b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1197,30 +1197,30 @@ "is_secret": false } ], - "website/_blogs/2023-07-14-Local-LLMs/index.mdx": [ + "website/docs/_blogs/2023-07-14-Local-LLMs/index.mdx": [ { "type": "Secret Keyword", - "filename": "website/_blogs/2023-07-14-Local-LLMs/index.mdx", + "filename": "website/docs/_blogs/2023-07-14-Local-LLMs/index.mdx", "hashed_secret": "eef19c54306daa69eda49c0272623bdb5e2b341f", "is_verified": false, "line_number": 83, "is_secret": false } ], - "website/_blogs/2023-10-18-RetrieveChat/index.mdx": [ + "website/docs/_blogs/2023-10-18-RetrieveChat/index.mdx": [ { "type": "Secret Keyword", - "filename": "website/_blogs/2023-10-18-RetrieveChat/index.mdx", + "filename": "website/docs/_blogs/2023-10-18-RetrieveChat/index.mdx", "hashed_secret": "1e3667aaaaa887721550cf5cc8a0c5c5760810ed", "is_verified": false, "line_number": 155, "is_secret": false } ], - "website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx": [ + "website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx": [ { "type": "Base64 High Entropy String", - "filename": "website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx", + "filename": "website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx", "hashed_secret": "76c1da7555caa23cfbad2a5a25caa463e8a81b30", "is_verified": false, "line_number": 46, @@ -1228,17 +1228,17 @@ }, { "type": "Secret Keyword", - "filename": "website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx", + "filename": "website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx", "hashed_secret": "5bc604777adb22ae457a5473c397202e00689e23", "is_verified": false, "line_number": 174, "is_secret": false } ], - "website/_blogs/2024-06-24-AltModels-Classes/index.mdx": [ + "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx": [ { "type": "Secret Keyword", - "filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx", + "filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx", "hashed_secret": "7d80773e54e49b5cc36d28a4a7baf9fff268491e", "is_verified": false, "line_number": 89, @@ -1246,7 +1246,7 @@ }, { "type": "Secret Keyword", - "filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx", + "filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx", "hashed_secret": "5f7eb17aeb646b3cda7af7c395fc9e291c62273e", "is_verified": false, "line_number": 94, @@ -1254,7 +1254,7 @@ }, { "type": "Secret Keyword", - "filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx", + "filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx", "hashed_secret": "9918921badd6ab97ccf2355ca3dda0fefbf79d03", "is_verified": false, "line_number": 99, @@ -1262,7 +1262,7 @@ }, { "type": "Secret Keyword", - "filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx", + "filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx", "hashed_secret": "2da1eec5f20a39f9f7b1449ea3f3ad0abc84642c", "is_verified": false, "line_number": 104, @@ -1541,7 +1541,7 @@ "filename": "website/docs/user-guide/models/vLLM.mdx", "hashed_secret": "aa572c1a118d643cfe6442e2ab56f7f3bb5d63d2", "is_verified": false, - "line_number": 90, + "line_number": 91, "is_secret": false } ], diff --git a/autogen/_website/generate_mkdocs.py b/autogen/_website/generate_mkdocs.py new file mode 100644 index 0000000000..192e3f98f6 --- /dev/null +++ b/autogen/_website/generate_mkdocs.py @@ -0,0 +1,157 @@ +# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors +# +# SPDX-License-Identifier: Apache-2.0 + + +import json +import re +import shutil +from pathlib import Path + +from ..import_utils import optional_import_block, require_optional_import +from .utils import NavigationGroup, copy_files, get_git_tracked_and_untracked_files_in_directory + +with optional_import_block(): + from jinja2 import Template + + +def filter_excluded_files(files: list[Path], exclusion_list: list[str], website_dir: Path) -> list[Path]: + return [ + file + for file in files + if not any(str(file.relative_to(website_dir)).startswith(excl) for excl in exclusion_list) + ] + + +def copy_file(file: Path, mkdocs_output_dir: Path) -> None: + dest = mkdocs_output_dir / file.relative_to(file.parents[1]) + dest.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(file, dest) + + +def transform_content_for_mkdocs(content: str) -> str: + # Transform admonitions (Tip, Warning, Note) + tag_mappings = { + "Tip": "tip", + "Warning": "warning", + "Note": "note", + "Danger": "danger", + } + for html_tag, mkdocs_type in tag_mappings.items(): + pattern = f"<{html_tag}>(.*?)" + + def replacement(match): + inner_content = match.group(1).strip() + return f"!!! {mkdocs_type}\n {inner_content}" + + content = re.sub(pattern, replacement, content, flags=re.DOTALL) + + # Clean up style tags with double curly braces + style_pattern = r"style\s*=\s*{{\s*([^}]+)\s*}}" + + def style_replacement(match): + style_content = match.group(1).strip() + return f"style={{ {style_content} }}" + + content = re.sub(style_pattern, style_replacement, content) + + return content + + +def process_and_copy_files(input_dir: Path, output_dir: Path, files: list[Path]) -> None: + for file in files: + if file.suffix == ".mdx": + content = file.read_text() + processed_content = transform_content_for_mkdocs(content) + dest = output_dir / file.relative_to(input_dir).with_suffix(".md") + dest.parent.mkdir(parents=True, exist_ok=True) + dest.write_text(processed_content) + else: + copy_files(input_dir, output_dir, [file]) + # copy_file(file, output_dir) + + +def format_title(title: str, keywords: dict[str, str]) -> str: + """Format a page title with proper capitalization for special keywords.""" + words = title.replace("-", " ").title().split() + return " ".join(keywords.get(word, word) for word in words) + + +def format_page_entry(page_path: str, indent: str, keywords: dict[str, str]) -> str: + """Format a single page entry as either a parenthesized path or a markdown link.""" + path = f"{page_path}.md" + title = format_title(Path(page_path).name, keywords) + return f"{indent} - [{title}]({path})" + + +def format_navigation(nav: list[NavigationGroup], depth: int = 0, keywords: dict[str, str] = None) -> str: + """ + Recursively format navigation structure into markdown-style nested list. + + Args: + nav: List of navigation items with groups and pages + depth: Current indentation depth + keywords: Dictionary of special case word capitalizations + + Returns: + Formatted navigation as a string + """ + if keywords is None: + keywords = { + "Ag2": "AG2", + "Rag": "RAG", + "Llm": "LLM", + } + + indent = " " * depth + result = [] + + for item in nav: + # Add group header + result.append(f"{indent}- {item['group']}") + + # Process each page + for page in item["pages"]: + if isinstance(page, dict): + # Handle nested navigation groups + result.append(format_navigation([page], depth + 1, keywords)) + else: + # Handle individual pages + result.append(format_page_entry(page, indent, keywords)) + + return "\n".join(result) + + +@require_optional_import("jinja2", "docs") +def generate_mkdocs_navigation(website_dir: Path, mkdocs_root_dir: Path, nav_exclusions: list[str]) -> None: + mintlify_nav_template_path = website_dir / "mint-json-template.json.jinja" + mkdocs_nav_path = mkdocs_root_dir / "docs" / "navigation_template.txt" + summary_md_path = mkdocs_root_dir / "docs" / "SUMMARY.md" + + mintlify_json = json.loads(Template(mintlify_nav_template_path.read_text(encoding="utf-8")).render()) + mintlify_nav = mintlify_json["navigation"] + filtered_nav = [item for item in mintlify_nav if item["group"] not in nav_exclusions] + + mkdocs_nav_content = "---\nsearch:\n exclude: true\n---\n" + format_navigation(filtered_nav) + "\n" + mkdocs_nav_path.write_text(mkdocs_nav_content) + summary_md_path.write_text(mkdocs_nav_content) + + +def main() -> None: + root_dir = Path(__file__).resolve().parents[2] + website_dir = root_dir / "website" + + mint_inpur_dir = website_dir / "docs" + + mkdocs_root_dir = website_dir / "mkdocs" + mkdocs_output_dir = mkdocs_root_dir / "docs" / "docs" + + exclusion_list = ["docs/_blogs", "docs/home", "docs/.gitignore", "docs/use-cases"] + nav_exclusions = ["Use Cases"] + + files_to_copy = get_git_tracked_and_untracked_files_in_directory(mint_inpur_dir) + filtered_files = filter_excluded_files(files_to_copy, exclusion_list, website_dir) + + process_and_copy_files(mint_inpur_dir, mkdocs_output_dir, filtered_files) + + generate_mkdocs_navigation(website_dir, mkdocs_root_dir, nav_exclusions) diff --git a/autogen/_website/process_notebooks.py b/autogen/_website/process_notebooks.py index 1334d605ee..89a343aea0 100755 --- a/autogen/_website/process_notebooks.py +++ b/autogen/_website/process_notebooks.py @@ -27,9 +27,10 @@ from functools import lru_cache from pathlib import Path from textwrap import dedent, indent -from typing import Any, Callable, Optional, Sequence, TypeVar, TypedDict, Union +from typing import Any, Callable, Optional, Sequence, TypeVar, Union from ..import_utils import optional_import_block, require_optional_import +from .utils import NavigationGroup with optional_import_block(): import nbformat @@ -802,7 +803,7 @@ def add_notebooks_blogs_and_user_stories_to_nav(website_build_directory: Path) - mint_config["navigation"].append(user_stories_section) # add blogs to navigation - blogs_dir = website_build_directory / "_blogs" + blogs_dir = website_build_directory / "docs" / "_blogs" blog_section = {"group": "Blog", "pages": [generate_nav_group(blogs_dir, "Recent posts", "docs/blog")]} mint_config["navigation"].append(blog_section) @@ -972,7 +973,7 @@ def _add_authors_and_social_preview( rel_file_path = ( str(file_path.relative_to(website_build_dir.parent)) .replace("build/docs/", "website/docs/") - .replace("website/docs/blog/", "website/_blogs/") + .replace("website/docs/blog/", "website/docs/_blogs/") ) content_with_edit_url = ensure_edit_url(new_content, Path(rel_file_path)) @@ -989,7 +990,7 @@ def add_authors_and_social_img_to_blog_and_user_stories(website_build_directory: Args: website_build_directory (Path): Build directory of the website """ - blog_dir = website_build_directory / "_blogs" + blog_dir = website_build_directory / "docs" / "_blogs" generated_blog_dir = website_build_directory / "docs" / "blog" authors_yml = website_build_directory / "blogs_and_user_stories_authors.yml" @@ -1035,11 +1036,6 @@ def cleanup_tmp_dirs(website_build_directory: Path, re_generate_notebooks: bool) shutil.rmtree(notebooks_dir, ignore_errors=True) -class NavigationGroup(TypedDict): - group: str - pages: list[Union[str, "NavigationGroup"]] - - def get_files_path_from_navigation(navigation: list[NavigationGroup]) -> list[Path]: """Extract all file paths from the navigation structure. diff --git a/autogen/_website/utils.py b/autogen/_website/utils.py index 992650dbb1..e2038bd15d 100644 --- a/autogen/_website/utils.py +++ b/autogen/_website/utils.py @@ -5,6 +5,12 @@ import shutil import subprocess from pathlib import Path +from typing import TypedDict, Union + + +class NavigationGroup(TypedDict): + group: str + pages: list[Union[str, "NavigationGroup"]] def get_git_tracked_and_untracked_files_in_directory(directory: Path) -> set[Path]: @@ -18,12 +24,16 @@ def get_git_tracked_and_untracked_files_in_directory(directory: Path) -> set[Pat return {directory / p for p in proc.stdout.splitlines()} +def copy_files(src_dir: Path, dst_dir: Path, files_to_copy: list[Path]) -> None: + """Copy files from src_dir to dst_dir.""" + for file in files_to_copy: + if file.is_file(): + dst = dst_dir / file.relative_to(src_dir) + dst.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(file, dst) + + def copy_only_git_tracked_and_untracked_files(src_dir: Path, dst_dir: Path) -> None: """Copy only the files that are tracked by git or newly added from src_dir to dst_dir.""" tracked_and_new_files = get_git_tracked_and_untracked_files_in_directory(src_dir) - - for src in tracked_and_new_files: - if src.is_file(): - dst = dst_dir / src.relative_to(src_dir) - dst.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(src, dst) + copy_files(src_dir, dst_dir, tracked_and_new_files) diff --git a/pyproject.toml b/pyproject.toml index 17d8b0d89d..9eda1bc6f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -221,8 +221,19 @@ test = [ "fastapi==0.115.8", ] -# docs dependencies docs = [ + "mkdocs-material==9.6.4", + "mkdocstrings[python]==0.28.1", + "mkdocs-literate-nav==0.6.1", + "mdx-include==1.4.2", + "mkdocs-git-revision-date-localized-plugin==1.3.0", + "mike==2.1.3", + "typer==0.15.1", + "mkdocs-minify-plugin==0.8.0", + "mkdocs-macros-plugin==1.3.7", # includes with variables + "mkdocs-glightbox==0.4.0", # img zoom + "pillow", # required for mkdocs-glightbo + "cairosvg", # required for mkdocs-glightbo "pdoc3==0.11.5", "jinja2==3.1.5", "pyyaml==6.0.2", diff --git a/scripts/docs_build_mkdocs.sh b/scripts/docs_build_mkdocs.sh new file mode 100755 index 0000000000..1cf3a8b016 --- /dev/null +++ b/scripts/docs_build_mkdocs.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -e +set -x + +cd website/mkdocs; python docs.py build diff --git a/scripts/docs_serve_mkdocs.sh b/scripts/docs_serve_mkdocs.sh new file mode 100755 index 0000000000..f61cef999e --- /dev/null +++ b/scripts/docs_serve_mkdocs.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -e +set -x + +cd website/mkdocs; python docs.py live "$@" diff --git a/test/website/test_generate_mkdocs.py b/test/website/test_generate_mkdocs.py new file mode 100644 index 0000000000..aef59282b2 --- /dev/null +++ b/test/website/test_generate_mkdocs.py @@ -0,0 +1,236 @@ +# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors +# +# SPDX-License-Identifier: Apache-2.0 + +import json +import tempfile +from pathlib import Path +from textwrap import dedent + +import pytest + +from autogen._website.generate_mkdocs import ( + filter_excluded_files, + format_navigation, + generate_mkdocs_navigation, + process_and_copy_files, +) +from autogen._website.utils import NavigationGroup +from autogen.import_utils import optional_import_block, skip_on_missing_imports + +with optional_import_block(): + import jinja2 + + assert jinja2 + + +def test_exclude_files() -> None: + files = [ + Path("/tmp/ag2/ag2/website/docs/user-guide/advanced-concepts/groupchat/groupchat.mdx"), + Path("/tmp/ag2/ag2/website/docs/user-guide/advanced-concepts/groupchat/chat.txt"), + Path("/tmp/ag2/ag2/website/docs/_blogs/2023-04-21-LLM-tuning-math/index.mdx"), + Path("/tmp/ag2/ag2/website/docs/home/home.mdx"), + Path("/tmp/ag2/ag2/website/docs/home/quick-start.mdx"), + ] + + exclusion_list = ["docs/_blogs", "docs/home"] + website_dir = Path("/tmp/ag2/ag2/website") + + actual = filter_excluded_files(files, exclusion_list, website_dir) + expected = files[:2] + assert actual == expected + + +def test_process_and_copy_files() -> None: + with tempfile.TemporaryDirectory() as tmpdir: + # Create source directory structure + src_dir = Path(tmpdir) / "src" + src_dir.mkdir() + + files = [ + src_dir / "user-guide" / "advanced-concepts" / "groupchat" / "groupchat.mdx", + src_dir / "user-guide" / "advanced-concepts" / "groupchat" / "chat.txt", + src_dir / "home" / "agent.png", + src_dir / "home" / "quick-start.mdx", + ] + # Create the content for quick-start.mdx + quick_start_content = dedent(""" + + It is important to never hard-code secrets into your code, therefore we read the OpenAI API key from an environment variable. + + + + It is important to never hard-code secrets into your code, therefore we read the OpenAI API key from an environment variable. + + + + It is important to never hard-code secrets into your code, therefore we read the OpenAI API key from an environment variable. + + + AgentOps logo + + """).lstrip() + + for file in files: + file.parent.mkdir(parents=True, exist_ok=True) + if file.name == "quick-start.mdx": + file.write_text(quick_start_content) + else: + file.touch() + + mkdocs_output_dir = Path(tmpdir) / "mkdocs_output" + mkdocs_output_dir.mkdir() + + process_and_copy_files(src_dir, mkdocs_output_dir, files) + + actual = list(filter(lambda x: x.is_file(), mkdocs_output_dir.rglob("*"))) + expected = [ + mkdocs_output_dir / "home" / "agent.png", + mkdocs_output_dir / "home" / "quick-start.md", + mkdocs_output_dir / "user-guide" / "advanced-concepts" / "groupchat" / "chat.txt", + mkdocs_output_dir / "user-guide" / "advanced-concepts" / "groupchat" / "groupchat.md", + ] + assert len(actual) == len(expected) + assert sorted(actual) == sorted(actual) + + # Assert the content of the transformed markdown file + expected_quick_start_content = dedent(""" + !!! tip + It is important to never hard-code secrets into your code, therefore we read the OpenAI API key from an environment variable. + + !!! warning + It is important to never hard-code secrets into your code, therefore we read the OpenAI API key from an environment variable. + + !!! note + It is important to never hard-code secrets into your code, therefore we read the OpenAI API key from an environment variable. + + AgentOps logo + + """).lstrip() + + with open(mkdocs_output_dir / "home" / "quick-start.md") as f: + actual_quick_start_content = f.read() + + assert actual_quick_start_content == expected_quick_start_content + + +@pytest.fixture +def navigation() -> list[NavigationGroup]: + return [ + {"group": "Home", "pages": ["docs/home/home", "docs/home/quick-start"]}, + { + "group": "User Guide", + "pages": [ + { + "group": "Basic Concepts", + "pages": [ + "docs/user-guide/basic-concepts/installing-ag2", + { + "group": "LLM Configuration", + "pages": [ + "docs/user-guide/basic-concepts/llm-configuration/llm-configuration", + "docs/user-guide/basic-concepts/llm-configuration/structured-outputs", + ], + }, + "docs/user-guide/basic-concepts/conversable-agent", + "docs/user-guide/basic-concepts/human-in-the-loop", + { + "group": "Orchestrating Agents", + "pages": [ + "docs/user-guide/basic-concepts/orchestration/orchestrations", + "docs/user-guide/basic-concepts/orchestration/sequential-chat", + ], + }, + ], + }, + {"group": "Advanced Concepts", "pages": ["docs/user-guide/advanced-concepts/rag"]}, + ], + }, + { + "group": "Contributing", + "pages": [ + "docs/contributing/contributing", + ], + }, + ] + + +@pytest.fixture +def expected_nav() -> str: + return """- Home + - [Home](docs/home/home.md) + - [Quick Start](docs/home/quick-start.md) +- User Guide + - Basic Concepts + - [Installing AG2](docs/user-guide/basic-concepts/installing-ag2.md) + - LLM Configuration + - [LLM Configuration](docs/user-guide/basic-concepts/llm-configuration/llm-configuration.md) + - [Structured Outputs](docs/user-guide/basic-concepts/llm-configuration/structured-outputs.md) + - [Conversable Agent](docs/user-guide/basic-concepts/conversable-agent.md) + - [Human In The Loop](docs/user-guide/basic-concepts/human-in-the-loop.md) + - Orchestrating Agents + - [Orchestrations](docs/user-guide/basic-concepts/orchestration/orchestrations.md) + - [Sequential Chat](docs/user-guide/basic-concepts/orchestration/sequential-chat.md) + - Advanced Concepts + - [RAG](docs/user-guide/advanced-concepts/rag.md) +- Contributing + - [Contributing](docs/contributing/contributing.md)""" + + +def test_format_navigation(navigation: list[NavigationGroup], expected_nav: str) -> None: + actual = format_navigation(navigation) + assert actual == expected_nav + + +# The commented out code `# @skip_on_missing_imports(["jinja2"], "docs")` is likely a decorator that +# is used to skip a test if certain imports are missing. In this case, it seems to be checking if the +# `jinja2` library is missing before running the test function `test_generate_mkdocs_navigation`. +@skip_on_missing_imports(["jinja2"], "docs") +def test_generate_mkdocs_navigation(navigation: list[NavigationGroup], expected_nav: str) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + # Create source directory structure + website_dir = Path(tmpdir) / "website_root" + website_dir.mkdir() + + # Create mkdocs directory + mkdocs_root_dir = Path(tmpdir) / "mkdocs_root" + mkdocs_root_dir.mkdir() + + mintlify_nav_template_path = website_dir / "mint-json-template.json.jinja" + mkdocs_nav_path = mkdocs_root_dir / "docs" / "navigation_template.txt" + mkdocs_nav_path.parent.mkdir(parents=True, exist_ok=True) + mkdocs_nav_path.touch() + + summary_md_path = mkdocs_root_dir / "docs" / "SUMMARY.md" + + mintlify_nav_content = ( + """ + { + "$schema": "https://mintlify.com/schema.json", + "name": "AG2", + "navigation": """ + + json.dumps(navigation) + + """ }""" + ) + + mintlify_nav_template_path.write_text(mintlify_nav_content) + + nav_exclusions = ["Contributing"] + generate_mkdocs_navigation(website_dir, mkdocs_root_dir, nav_exclusions) + actual = mkdocs_nav_path.read_text() + expected = ( + """--- +search: + exclude: true +--- +""" + + expected_nav.replace( + """ +- Contributing + - [Contributing](docs/contributing/contributing.md)""", + "", + ) + + "\n" + ) + assert actual == expected + assert summary_md_path.read_text() == expected diff --git a/test/website/test_process_notebooks.py b/test/website/test_process_notebooks.py index 168d13f8c1..371faa273b 100644 --- a/test/website/test_process_notebooks.py +++ b/test/website/test_process_notebooks.py @@ -12,7 +12,6 @@ import pytest from autogen._website.process_notebooks import ( - NavigationGroup, add_authors_and_social_img_to_blog_and_user_stories, add_front_matter_to_metadata_mdx, cleanup_tmp_dirs, @@ -26,6 +25,7 @@ get_sorted_files, update_group_pages, ) +from autogen._website.utils import NavigationGroup from autogen.import_utils import skip_on_missing_imports @@ -474,8 +474,8 @@ def test_dir(self) -> Generator[Path, None, None]: """Create temporary test directory with blog posts and authors file.""" with tempfile.TemporaryDirectory() as tmp_dir: website_dir = Path(tmp_dir) - blog_dir = website_dir / "_blogs" - blog_dir.mkdir() + blog_dir = website_dir / "docs" / "_blogs" + blog_dir.mkdir(parents=True) # Create first blog post post1_dir = blog_dir / "2023-04-21-LLM-tuning-math" @@ -608,7 +608,7 @@ def test_add_authors_and_social_img(self, test_dir: Path) -> None: # Get directory paths generated_blog_dir = test_dir / "docs" / "blog" - blog_dir = test_dir / "_blogs" + blog_dir = test_dir / "docs" / "_blogs" # Verify directory structure matches blog_files = set(p.relative_to(blog_dir) for p in blog_dir.glob("**/*.mdx")) diff --git a/test/website/test_utils.py b/test/website/test_utils.py new file mode 100644 index 0000000000..370c941d5f --- /dev/null +++ b/test/website/test_utils.py @@ -0,0 +1,35 @@ +# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors +# +# SPDX-License-Identifier: Apache-2.0 + + +from pathlib import Path + +from autogen._website.utils import copy_files + + +def test_copy_files(tmp_path: Path) -> None: + # Create source directory structure + src_dir = tmp_path / "src" + src_dir.mkdir() + + # Create some test files in source + test_files = [src_dir / "file1.txt", src_dir / "subdir" / "file2.txt"] + + # Create the subdir and files + (src_dir / "subdir").mkdir(parents=True) + for file in test_files: + file.write_text("test content") + + # Create destination directory + dst_dir = tmp_path / "dst" + dst_dir.mkdir() + + # Call the function + copy_files(src_dir, dst_dir, test_files) + + # Verify files were copied correctly + for src_file in test_files: + dst_file = dst_dir / src_file.relative_to(src_dir) + assert dst_file.exists() + assert dst_file.read_text() == "test content" diff --git a/website/_blogs/2023-04-21-LLM-tuning-math/img/level2algebra.png b/website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level2algebra.png similarity index 100% rename from website/_blogs/2023-04-21-LLM-tuning-math/img/level2algebra.png rename to website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level2algebra.png diff --git a/website/_blogs/2023-04-21-LLM-tuning-math/img/level3algebra.png b/website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level3algebra.png similarity index 100% rename from website/_blogs/2023-04-21-LLM-tuning-math/img/level3algebra.png rename to website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level3algebra.png diff --git a/website/_blogs/2023-04-21-LLM-tuning-math/img/level4algebra.png b/website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level4algebra.png similarity index 100% rename from website/_blogs/2023-04-21-LLM-tuning-math/img/level4algebra.png rename to website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level4algebra.png diff --git a/website/_blogs/2023-04-21-LLM-tuning-math/img/level5algebra.png b/website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level5algebra.png similarity index 100% rename from website/_blogs/2023-04-21-LLM-tuning-math/img/level5algebra.png rename to website/docs/_blogs/2023-04-21-LLM-tuning-math/img/level5algebra.png diff --git a/website/_blogs/2023-04-21-LLM-tuning-math/index.mdx b/website/docs/_blogs/2023-04-21-LLM-tuning-math/index.mdx similarity index 100% rename from website/_blogs/2023-04-21-LLM-tuning-math/index.mdx rename to website/docs/_blogs/2023-04-21-LLM-tuning-math/index.mdx diff --git a/website/_blogs/2023-05-18-GPT-adaptive-humaneval/img/design.png b/website/docs/_blogs/2023-05-18-GPT-adaptive-humaneval/img/design.png similarity index 100% rename from website/_blogs/2023-05-18-GPT-adaptive-humaneval/img/design.png rename to website/docs/_blogs/2023-05-18-GPT-adaptive-humaneval/img/design.png diff --git a/website/_blogs/2023-05-18-GPT-adaptive-humaneval/img/humaneval.png b/website/docs/_blogs/2023-05-18-GPT-adaptive-humaneval/img/humaneval.png similarity index 100% rename from website/_blogs/2023-05-18-GPT-adaptive-humaneval/img/humaneval.png rename to website/docs/_blogs/2023-05-18-GPT-adaptive-humaneval/img/humaneval.png diff --git a/website/_blogs/2023-05-18-GPT-adaptive-humaneval/index.mdx b/website/docs/_blogs/2023-05-18-GPT-adaptive-humaneval/index.mdx similarity index 100% rename from website/_blogs/2023-05-18-GPT-adaptive-humaneval/index.mdx rename to website/docs/_blogs/2023-05-18-GPT-adaptive-humaneval/index.mdx diff --git a/website/_blogs/2023-06-28-MathChat/img/mathchatflow.png b/website/docs/_blogs/2023-06-28-MathChat/img/mathchatflow.png similarity index 100% rename from website/_blogs/2023-06-28-MathChat/img/mathchatflow.png rename to website/docs/_blogs/2023-06-28-MathChat/img/mathchatflow.png diff --git a/website/_blogs/2023-06-28-MathChat/img/result.png b/website/docs/_blogs/2023-06-28-MathChat/img/result.png similarity index 100% rename from website/_blogs/2023-06-28-MathChat/img/result.png rename to website/docs/_blogs/2023-06-28-MathChat/img/result.png diff --git a/website/_blogs/2023-06-28-MathChat/index.mdx b/website/docs/_blogs/2023-06-28-MathChat/index.mdx similarity index 100% rename from website/_blogs/2023-06-28-MathChat/index.mdx rename to website/docs/_blogs/2023-06-28-MathChat/index.mdx diff --git a/website/_blogs/2023-07-14-Local-LLMs/index.mdx b/website/docs/_blogs/2023-07-14-Local-LLMs/index.mdx similarity index 100% rename from website/_blogs/2023-07-14-Local-LLMs/index.mdx rename to website/docs/_blogs/2023-07-14-Local-LLMs/index.mdx diff --git a/website/_blogs/2023-10-18-RetrieveChat/img/autogen-rag.gif b/website/docs/_blogs/2023-10-18-RetrieveChat/img/autogen-rag.gif similarity index 100% rename from website/_blogs/2023-10-18-RetrieveChat/img/autogen-rag.gif rename to website/docs/_blogs/2023-10-18-RetrieveChat/img/autogen-rag.gif diff --git a/website/_blogs/2023-10-18-RetrieveChat/img/retrievechat-arch.png b/website/docs/_blogs/2023-10-18-RetrieveChat/img/retrievechat-arch.png similarity index 100% rename from website/_blogs/2023-10-18-RetrieveChat/img/retrievechat-arch.png rename to website/docs/_blogs/2023-10-18-RetrieveChat/img/retrievechat-arch.png diff --git a/website/_blogs/2023-10-18-RetrieveChat/index.mdx b/website/docs/_blogs/2023-10-18-RetrieveChat/index.mdx similarity index 100% rename from website/_blogs/2023-10-18-RetrieveChat/index.mdx rename to website/docs/_blogs/2023-10-18-RetrieveChat/index.mdx diff --git a/website/_blogs/2023-10-26-TeachableAgent/img/teachable-arch.png b/website/docs/_blogs/2023-10-26-TeachableAgent/img/teachable-arch.png similarity index 100% rename from website/_blogs/2023-10-26-TeachableAgent/img/teachable-arch.png rename to website/docs/_blogs/2023-10-26-TeachableAgent/img/teachable-arch.png diff --git a/website/_blogs/2023-10-26-TeachableAgent/index.mdx b/website/docs/_blogs/2023-10-26-TeachableAgent/index.mdx similarity index 100% rename from website/_blogs/2023-10-26-TeachableAgent/index.mdx rename to website/docs/_blogs/2023-10-26-TeachableAgent/index.mdx diff --git a/website/_blogs/2023-11-06-LMM-Agent/img/teaser.png b/website/docs/_blogs/2023-11-06-LMM-Agent/img/teaser.png similarity index 100% rename from website/_blogs/2023-11-06-LMM-Agent/img/teaser.png rename to website/docs/_blogs/2023-11-06-LMM-Agent/img/teaser.png diff --git a/website/_blogs/2023-11-06-LMM-Agent/index.mdx b/website/docs/_blogs/2023-11-06-LMM-Agent/index.mdx similarity index 100% rename from website/_blogs/2023-11-06-LMM-Agent/index.mdx rename to website/docs/_blogs/2023-11-06-LMM-Agent/index.mdx diff --git a/website/_blogs/2023-11-09-EcoAssistant/img/chat.webp b/website/docs/_blogs/2023-11-09-EcoAssistant/img/chat.webp similarity index 100% rename from website/_blogs/2023-11-09-EcoAssistant/img/chat.webp rename to website/docs/_blogs/2023-11-09-EcoAssistant/img/chat.webp diff --git a/website/_blogs/2023-11-09-EcoAssistant/img/results.png b/website/docs/_blogs/2023-11-09-EcoAssistant/img/results.png similarity index 100% rename from website/_blogs/2023-11-09-EcoAssistant/img/results.png rename to website/docs/_blogs/2023-11-09-EcoAssistant/img/results.png diff --git a/website/_blogs/2023-11-09-EcoAssistant/img/system.webp b/website/docs/_blogs/2023-11-09-EcoAssistant/img/system.webp similarity index 100% rename from website/_blogs/2023-11-09-EcoAssistant/img/system.webp rename to website/docs/_blogs/2023-11-09-EcoAssistant/img/system.webp diff --git a/website/_blogs/2023-11-09-EcoAssistant/img/template-demo.png b/website/docs/_blogs/2023-11-09-EcoAssistant/img/template-demo.png similarity index 100% rename from website/_blogs/2023-11-09-EcoAssistant/img/template-demo.png rename to website/docs/_blogs/2023-11-09-EcoAssistant/img/template-demo.png diff --git a/website/_blogs/2023-11-09-EcoAssistant/img/template.png b/website/docs/_blogs/2023-11-09-EcoAssistant/img/template.png similarity index 100% rename from website/_blogs/2023-11-09-EcoAssistant/img/template.png rename to website/docs/_blogs/2023-11-09-EcoAssistant/img/template.png diff --git a/website/_blogs/2023-11-09-EcoAssistant/index.mdx b/website/docs/_blogs/2023-11-09-EcoAssistant/index.mdx similarity index 100% rename from website/_blogs/2023-11-09-EcoAssistant/index.mdx rename to website/docs/_blogs/2023-11-09-EcoAssistant/index.mdx diff --git a/website/_blogs/2023-11-13-OAI-assistants/img/teaser.jpg b/website/docs/_blogs/2023-11-13-OAI-assistants/img/teaser.jpg similarity index 100% rename from website/_blogs/2023-11-13-OAI-assistants/img/teaser.jpg rename to website/docs/_blogs/2023-11-13-OAI-assistants/img/teaser.jpg diff --git a/website/_blogs/2023-11-13-OAI-assistants/index.mdx b/website/docs/_blogs/2023-11-13-OAI-assistants/index.mdx similarity index 100% rename from website/_blogs/2023-11-13-OAI-assistants/index.mdx rename to website/docs/_blogs/2023-11-13-OAI-assistants/index.mdx diff --git a/website/_blogs/2023-11-20-AgentEval/img/agenteval-CQ.webp b/website/docs/_blogs/2023-11-20-AgentEval/img/agenteval-CQ.webp similarity index 100% rename from website/_blogs/2023-11-20-AgentEval/img/agenteval-CQ.webp rename to website/docs/_blogs/2023-11-20-AgentEval/img/agenteval-CQ.webp diff --git a/website/_blogs/2023-11-20-AgentEval/img/math-problems-plot.png b/website/docs/_blogs/2023-11-20-AgentEval/img/math-problems-plot.png similarity index 100% rename from website/_blogs/2023-11-20-AgentEval/img/math-problems-plot.png rename to website/docs/_blogs/2023-11-20-AgentEval/img/math-problems-plot.png diff --git a/website/_blogs/2023-11-20-AgentEval/img/tasks-taxonomy.webp b/website/docs/_blogs/2023-11-20-AgentEval/img/tasks-taxonomy.webp similarity index 100% rename from website/_blogs/2023-11-20-AgentEval/img/tasks-taxonomy.webp rename to website/docs/_blogs/2023-11-20-AgentEval/img/tasks-taxonomy.webp diff --git a/website/_blogs/2023-11-20-AgentEval/index.mdx b/website/docs/_blogs/2023-11-20-AgentEval/index.mdx similarity index 100% rename from website/_blogs/2023-11-20-AgentEval/index.mdx rename to website/docs/_blogs/2023-11-20-AgentEval/index.mdx diff --git a/website/_blogs/2023-11-26-Agent-AutoBuild/img/agent_autobuild.webp b/website/docs/_blogs/2023-11-26-Agent-AutoBuild/img/agent_autobuild.webp similarity index 100% rename from website/_blogs/2023-11-26-Agent-AutoBuild/img/agent_autobuild.webp rename to website/docs/_blogs/2023-11-26-Agent-AutoBuild/img/agent_autobuild.webp diff --git a/website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx b/website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx similarity index 100% rename from website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx rename to website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx diff --git a/website/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_config.png b/website/docs/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_config.png similarity index 100% rename from website/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_config.png rename to website/docs/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_config.png diff --git a/website/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_home.png b/website/docs/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_home.png similarity index 100% rename from website/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_home.png rename to website/docs/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_home.png diff --git a/website/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_skills.png b/website/docs/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_skills.png similarity index 100% rename from website/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_skills.png rename to website/docs/_blogs/2023-12-01-AutoGenStudio/img/autogenstudio_skills.png diff --git a/website/_blogs/2023-12-01-AutoGenStudio/index.mdx b/website/docs/_blogs/2023-12-01-AutoGenStudio/index.mdx similarity index 100% rename from website/_blogs/2023-12-01-AutoGenStudio/index.mdx rename to website/docs/_blogs/2023-12-01-AutoGenStudio/index.mdx diff --git a/website/_blogs/2023-12-23-AgentOptimizer/img/agentoptimizer.webp b/website/docs/_blogs/2023-12-23-AgentOptimizer/img/agentoptimizer.webp similarity index 100% rename from website/_blogs/2023-12-23-AgentOptimizer/img/agentoptimizer.webp rename to website/docs/_blogs/2023-12-23-AgentOptimizer/img/agentoptimizer.webp diff --git a/website/_blogs/2023-12-23-AgentOptimizer/index.mdx b/website/docs/_blogs/2023-12-23-AgentOptimizer/index.mdx similarity index 100% rename from website/_blogs/2023-12-23-AgentOptimizer/index.mdx rename to website/docs/_blogs/2023-12-23-AgentOptimizer/index.mdx diff --git a/website/_blogs/2023-12-29-AgentDescriptions/index.mdx b/website/docs/_blogs/2023-12-29-AgentDescriptions/index.mdx similarity index 100% rename from website/_blogs/2023-12-29-AgentDescriptions/index.mdx rename to website/docs/_blogs/2023-12-29-AgentDescriptions/index.mdx diff --git a/website/_blogs/2024-01-23-Code-execution-in-docker/index.mdx b/website/docs/_blogs/2024-01-23-Code-execution-in-docker/index.mdx similarity index 100% rename from website/_blogs/2024-01-23-Code-execution-in-docker/index.mdx rename to website/docs/_blogs/2024-01-23-Code-execution-in-docker/index.mdx diff --git a/website/_blogs/2024-01-25-AutoGenBench/img/teaser.jpg b/website/docs/_blogs/2024-01-25-AutoGenBench/img/teaser.jpg similarity index 100% rename from website/_blogs/2024-01-25-AutoGenBench/img/teaser.jpg rename to website/docs/_blogs/2024-01-25-AutoGenBench/img/teaser.jpg diff --git a/website/_blogs/2024-01-25-AutoGenBench/index.mdx b/website/docs/_blogs/2024-01-25-AutoGenBench/index.mdx similarity index 100% rename from website/_blogs/2024-01-25-AutoGenBench/index.mdx rename to website/docs/_blogs/2024-01-25-AutoGenBench/index.mdx diff --git a/website/_blogs/2024-01-26-Custom-Models/index.mdx b/website/docs/_blogs/2024-01-26-Custom-Models/index.mdx similarity index 100% rename from website/_blogs/2024-01-26-Custom-Models/index.mdx rename to website/docs/_blogs/2024-01-26-Custom-Models/index.mdx diff --git a/website/_blogs/2024-02-02-AutoAnny/img/AutoAnnyLogo.jpg b/website/docs/_blogs/2024-02-02-AutoAnny/img/AutoAnnyLogo.jpg similarity index 100% rename from website/_blogs/2024-02-02-AutoAnny/img/AutoAnnyLogo.jpg rename to website/docs/_blogs/2024-02-02-AutoAnny/img/AutoAnnyLogo.jpg diff --git a/website/_blogs/2024-02-02-AutoAnny/index.mdx b/website/docs/_blogs/2024-02-02-AutoAnny/index.mdx similarity index 100% rename from website/_blogs/2024-02-02-AutoAnny/index.mdx rename to website/docs/_blogs/2024-02-02-AutoAnny/index.mdx diff --git a/website/_blogs/2024-02-11-FSM-GroupChat/img/FSM_logic.webp b/website/docs/_blogs/2024-02-11-FSM-GroupChat/img/FSM_logic.webp similarity index 100% rename from website/_blogs/2024-02-11-FSM-GroupChat/img/FSM_logic.webp rename to website/docs/_blogs/2024-02-11-FSM-GroupChat/img/FSM_logic.webp diff --git a/website/_blogs/2024-02-11-FSM-GroupChat/img/FSM_of_multi-agents.webp b/website/docs/_blogs/2024-02-11-FSM-GroupChat/img/FSM_of_multi-agents.webp similarity index 100% rename from website/_blogs/2024-02-11-FSM-GroupChat/img/FSM_of_multi-agents.webp rename to website/docs/_blogs/2024-02-11-FSM-GroupChat/img/FSM_of_multi-agents.webp diff --git a/website/_blogs/2024-02-11-FSM-GroupChat/img/teaser.webp b/website/docs/_blogs/2024-02-11-FSM-GroupChat/img/teaser.webp similarity index 100% rename from website/_blogs/2024-02-11-FSM-GroupChat/img/teaser.webp rename to website/docs/_blogs/2024-02-11-FSM-GroupChat/img/teaser.webp diff --git a/website/_blogs/2024-02-11-FSM-GroupChat/index.mdx b/website/docs/_blogs/2024-02-11-FSM-GroupChat/index.mdx similarity index 100% rename from website/_blogs/2024-02-11-FSM-GroupChat/index.mdx rename to website/docs/_blogs/2024-02-11-FSM-GroupChat/index.mdx diff --git a/website/_blogs/2024-02-29-StateFlow/img/alfworld.png b/website/docs/_blogs/2024-02-29-StateFlow/img/alfworld.png similarity index 100% rename from website/_blogs/2024-02-29-StateFlow/img/alfworld.png rename to website/docs/_blogs/2024-02-29-StateFlow/img/alfworld.png diff --git a/website/_blogs/2024-02-29-StateFlow/img/bash_result.png b/website/docs/_blogs/2024-02-29-StateFlow/img/bash_result.png similarity index 100% rename from website/_blogs/2024-02-29-StateFlow/img/bash_result.png rename to website/docs/_blogs/2024-02-29-StateFlow/img/bash_result.png diff --git a/website/_blogs/2024-02-29-StateFlow/img/intercode.webp b/website/docs/_blogs/2024-02-29-StateFlow/img/intercode.webp similarity index 100% rename from website/_blogs/2024-02-29-StateFlow/img/intercode.webp rename to website/docs/_blogs/2024-02-29-StateFlow/img/intercode.webp diff --git a/website/_blogs/2024-02-29-StateFlow/img/sf_example_1.webp b/website/docs/_blogs/2024-02-29-StateFlow/img/sf_example_1.webp similarity index 100% rename from website/_blogs/2024-02-29-StateFlow/img/sf_example_1.webp rename to website/docs/_blogs/2024-02-29-StateFlow/img/sf_example_1.webp diff --git a/website/_blogs/2024-02-29-StateFlow/index.mdx b/website/docs/_blogs/2024-02-29-StateFlow/index.mdx similarity index 100% rename from website/_blogs/2024-02-29-StateFlow/index.mdx rename to website/docs/_blogs/2024-02-29-StateFlow/index.mdx diff --git a/website/_blogs/2024-03-03-AutoGen-Update/img/contributors.png b/website/docs/_blogs/2024-03-03-AutoGen-Update/img/contributors.png similarity index 100% rename from website/_blogs/2024-03-03-AutoGen-Update/img/contributors.png rename to website/docs/_blogs/2024-03-03-AutoGen-Update/img/contributors.png diff --git a/website/_blogs/2024-03-03-AutoGen-Update/img/dalle_gpt4v.png b/website/docs/_blogs/2024-03-03-AutoGen-Update/img/dalle_gpt4v.png similarity index 100% rename from website/_blogs/2024-03-03-AutoGen-Update/img/dalle_gpt4v.png rename to website/docs/_blogs/2024-03-03-AutoGen-Update/img/dalle_gpt4v.png diff --git a/website/_blogs/2024-03-03-AutoGen-Update/img/gaia.png b/website/docs/_blogs/2024-03-03-AutoGen-Update/img/gaia.png similarity index 100% rename from website/_blogs/2024-03-03-AutoGen-Update/img/gaia.png rename to website/docs/_blogs/2024-03-03-AutoGen-Update/img/gaia.png diff --git a/website/_blogs/2024-03-03-AutoGen-Update/img/love.png b/website/docs/_blogs/2024-03-03-AutoGen-Update/img/love.png similarity index 100% rename from website/_blogs/2024-03-03-AutoGen-Update/img/love.png rename to website/docs/_blogs/2024-03-03-AutoGen-Update/img/love.png diff --git a/website/_blogs/2024-03-03-AutoGen-Update/img/teach.png b/website/docs/_blogs/2024-03-03-AutoGen-Update/img/teach.png similarity index 100% rename from website/_blogs/2024-03-03-AutoGen-Update/img/teach.png rename to website/docs/_blogs/2024-03-03-AutoGen-Update/img/teach.png diff --git a/website/_blogs/2024-03-03-AutoGen-Update/index.mdx b/website/docs/_blogs/2024-03-03-AutoGen-Update/index.mdx similarity index 100% rename from website/_blogs/2024-03-03-AutoGen-Update/index.mdx rename to website/docs/_blogs/2024-03-03-AutoGen-Update/index.mdx diff --git a/website/_blogs/2024-03-11-AutoDefense/img/architecture.webp b/website/docs/_blogs/2024-03-11-AutoDefense/img/architecture.webp similarity index 100% rename from website/_blogs/2024-03-11-AutoDefense/img/architecture.webp rename to website/docs/_blogs/2024-03-11-AutoDefense/img/architecture.webp diff --git a/website/_blogs/2024-03-11-AutoDefense/img/defense-agency-design.webp b/website/docs/_blogs/2024-03-11-AutoDefense/img/defense-agency-design.webp similarity index 100% rename from website/_blogs/2024-03-11-AutoDefense/img/defense-agency-design.webp rename to website/docs/_blogs/2024-03-11-AutoDefense/img/defense-agency-design.webp diff --git a/website/_blogs/2024-03-11-AutoDefense/img/table-4agents.png b/website/docs/_blogs/2024-03-11-AutoDefense/img/table-4agents.png similarity index 100% rename from website/_blogs/2024-03-11-AutoDefense/img/table-4agents.png rename to website/docs/_blogs/2024-03-11-AutoDefense/img/table-4agents.png diff --git a/website/_blogs/2024-03-11-AutoDefense/img/table-agents.png b/website/docs/_blogs/2024-03-11-AutoDefense/img/table-agents.png similarity index 100% rename from website/_blogs/2024-03-11-AutoDefense/img/table-agents.png rename to website/docs/_blogs/2024-03-11-AutoDefense/img/table-agents.png diff --git a/website/_blogs/2024-03-11-AutoDefense/img/table-compared-methods.png b/website/docs/_blogs/2024-03-11-AutoDefense/img/table-compared-methods.png similarity index 100% rename from website/_blogs/2024-03-11-AutoDefense/img/table-compared-methods.png rename to website/docs/_blogs/2024-03-11-AutoDefense/img/table-compared-methods.png diff --git a/website/_blogs/2024-03-11-AutoDefense/index.mdx b/website/docs/_blogs/2024-03-11-AutoDefense/index.mdx similarity index 100% rename from website/_blogs/2024-03-11-AutoDefense/index.mdx rename to website/docs/_blogs/2024-03-11-AutoDefense/index.mdx diff --git a/website/_blogs/2024-05-24-Agent/img/agents.png b/website/docs/_blogs/2024-05-24-Agent/img/agents.png similarity index 100% rename from website/_blogs/2024-05-24-Agent/img/agents.png rename to website/docs/_blogs/2024-05-24-Agent/img/agents.png diff --git a/website/_blogs/2024-05-24-Agent/img/leadership.png b/website/docs/_blogs/2024-05-24-Agent/img/leadership.png similarity index 100% rename from website/_blogs/2024-05-24-Agent/img/leadership.png rename to website/docs/_blogs/2024-05-24-Agent/img/leadership.png diff --git a/website/_blogs/2024-05-24-Agent/index.mdx b/website/docs/_blogs/2024-05-24-Agent/index.mdx similarity index 100% rename from website/_blogs/2024-05-24-Agent/index.mdx rename to website/docs/_blogs/2024-05-24-Agent/index.mdx diff --git a/website/_blogs/2024-06-21-AgentEval/img/agenteval_ov_v3.webp b/website/docs/_blogs/2024-06-21-AgentEval/img/agenteval_ov_v3.webp similarity index 100% rename from website/_blogs/2024-06-21-AgentEval/img/agenteval_ov_v3.webp rename to website/docs/_blogs/2024-06-21-AgentEval/img/agenteval_ov_v3.webp diff --git a/website/_blogs/2024-06-21-AgentEval/index.mdx b/website/docs/_blogs/2024-06-21-AgentEval/index.mdx similarity index 100% rename from website/_blogs/2024-06-21-AgentEval/index.mdx rename to website/docs/_blogs/2024-06-21-AgentEval/index.mdx diff --git a/website/_blogs/2024-06-24-AltModels-Classes/img/agentstogether.jpeg b/website/docs/_blogs/2024-06-24-AltModels-Classes/img/agentstogether.jpeg similarity index 100% rename from website/_blogs/2024-06-24-AltModels-Classes/img/agentstogether.jpeg rename to website/docs/_blogs/2024-06-24-AltModels-Classes/img/agentstogether.jpeg diff --git a/website/_blogs/2024-06-24-AltModels-Classes/index.mdx b/website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx similarity index 100% rename from website/_blogs/2024-06-24-AltModels-Classes/index.mdx rename to website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx diff --git a/website/_blogs/2024-07-25-AgentOps/img/autogen-integration.png b/website/docs/_blogs/2024-07-25-AgentOps/img/autogen-integration.png similarity index 100% rename from website/_blogs/2024-07-25-AgentOps/img/autogen-integration.png rename to website/docs/_blogs/2024-07-25-AgentOps/img/autogen-integration.png diff --git a/website/_blogs/2024-07-25-AgentOps/img/dashboard.png b/website/docs/_blogs/2024-07-25-AgentOps/img/dashboard.png similarity index 100% rename from website/_blogs/2024-07-25-AgentOps/img/dashboard.png rename to website/docs/_blogs/2024-07-25-AgentOps/img/dashboard.png diff --git a/website/_blogs/2024-07-25-AgentOps/img/flow.png b/website/docs/_blogs/2024-07-25-AgentOps/img/flow.png similarity index 100% rename from website/_blogs/2024-07-25-AgentOps/img/flow.png rename to website/docs/_blogs/2024-07-25-AgentOps/img/flow.png diff --git a/website/_blogs/2024-07-25-AgentOps/img/session-replay.png b/website/docs/_blogs/2024-07-25-AgentOps/img/session-replay.png similarity index 100% rename from website/_blogs/2024-07-25-AgentOps/img/session-replay.png rename to website/docs/_blogs/2024-07-25-AgentOps/img/session-replay.png diff --git a/website/_blogs/2024-07-25-AgentOps/index.mdx b/website/docs/_blogs/2024-07-25-AgentOps/index.mdx similarity index 100% rename from website/_blogs/2024-07-25-AgentOps/index.mdx rename to website/docs/_blogs/2024-07-25-AgentOps/index.mdx diff --git a/website/_blogs/2024-11-15-CaptainAgent/img/build.webp b/website/docs/_blogs/2024-11-15-CaptainAgent/img/build.webp similarity index 100% rename from website/_blogs/2024-11-15-CaptainAgent/img/build.webp rename to website/docs/_blogs/2024-11-15-CaptainAgent/img/build.webp diff --git a/website/_blogs/2024-11-15-CaptainAgent/img/chat.webp b/website/docs/_blogs/2024-11-15-CaptainAgent/img/chat.webp similarity index 100% rename from website/_blogs/2024-11-15-CaptainAgent/img/chat.webp rename to website/docs/_blogs/2024-11-15-CaptainAgent/img/chat.webp diff --git a/website/_blogs/2024-11-15-CaptainAgent/img/overall.webp b/website/docs/_blogs/2024-11-15-CaptainAgent/img/overall.webp similarity index 100% rename from website/_blogs/2024-11-15-CaptainAgent/img/overall.webp rename to website/docs/_blogs/2024-11-15-CaptainAgent/img/overall.webp diff --git a/website/_blogs/2024-11-15-CaptainAgent/index.mdx b/website/docs/_blogs/2024-11-15-CaptainAgent/index.mdx similarity index 100% rename from website/_blogs/2024-11-15-CaptainAgent/index.mdx rename to website/docs/_blogs/2024-11-15-CaptainAgent/index.mdx diff --git a/website/_blogs/2024-11-17-Swarm/index.mdx b/website/docs/_blogs/2024-11-17-Swarm/index.mdx similarity index 100% rename from website/_blogs/2024-11-17-Swarm/index.mdx rename to website/docs/_blogs/2024-11-17-Swarm/index.mdx diff --git a/website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/probing_flow.webp b/website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/probing_flow.webp similarity index 100% rename from website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/probing_flow.webp rename to website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/probing_flow.webp diff --git a/website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_report.png b/website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_report.png similarity index 100% rename from website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_report.png rename to website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_report.png diff --git a/website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_social_img.webp b/website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_social_img.webp similarity index 100% rename from website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_social_img.webp rename to website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_leakage_social_img.webp diff --git a/website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_test_chat.webp b/website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_test_chat.webp similarity index 100% rename from website/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_test_chat.webp rename to website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/img/prompt_test_chat.webp diff --git a/website/_blogs/2024-11-27-Prompt-Leakage-Probing/index.mdx b/website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/index.mdx similarity index 100% rename from website/_blogs/2024-11-27-Prompt-Leakage-Probing/index.mdx rename to website/docs/_blogs/2024-11-27-Prompt-Leakage-Probing/index.mdx diff --git a/website/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_1.webp b/website/docs/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_1.webp similarity index 100% rename from website/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_1.webp rename to website/docs/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_1.webp diff --git a/website/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_2.webp b/website/docs/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_2.webp similarity index 100% rename from website/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_2.webp rename to website/docs/_blogs/2024-12-02-ReasoningAgent2/img/reasoningagent_2.webp diff --git a/website/_blogs/2024-12-02-ReasoningAgent2/img/tree-of-thoughts.png b/website/docs/_blogs/2024-12-02-ReasoningAgent2/img/tree-of-thoughts.png similarity index 100% rename from website/_blogs/2024-12-02-ReasoningAgent2/img/tree-of-thoughts.png rename to website/docs/_blogs/2024-12-02-ReasoningAgent2/img/tree-of-thoughts.png diff --git a/website/_blogs/2024-12-02-ReasoningAgent2/index.mdx b/website/docs/_blogs/2024-12-02-ReasoningAgent2/index.mdx similarity index 100% rename from website/_blogs/2024-12-02-ReasoningAgent2/index.mdx rename to website/docs/_blogs/2024-12-02-ReasoningAgent2/index.mdx diff --git a/website/_blogs/2024-12-06-FalkorDB-Structured/img/falkordb.png b/website/docs/_blogs/2024-12-06-FalkorDB-Structured/img/falkordb.png similarity index 100% rename from website/_blogs/2024-12-06-FalkorDB-Structured/img/falkordb.png rename to website/docs/_blogs/2024-12-06-FalkorDB-Structured/img/falkordb.png diff --git a/website/_blogs/2024-12-06-FalkorDB-Structured/img/tripplanner.webp b/website/docs/_blogs/2024-12-06-FalkorDB-Structured/img/tripplanner.webp similarity index 100% rename from website/_blogs/2024-12-06-FalkorDB-Structured/img/tripplanner.webp rename to website/docs/_blogs/2024-12-06-FalkorDB-Structured/img/tripplanner.webp diff --git a/website/_blogs/2024-12-06-FalkorDB-Structured/index.mdx b/website/docs/_blogs/2024-12-06-FalkorDB-Structured/index.mdx similarity index 100% rename from website/_blogs/2024-12-06-FalkorDB-Structured/index.mdx rename to website/docs/_blogs/2024-12-06-FalkorDB-Structured/index.mdx diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/1_service_running.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/1_service_running.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/1_service_running.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/1_service_running.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/2_incoming_call.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/2_incoming_call.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/2_incoming_call.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/2_incoming_call.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/3_request_for_flight_cancellation.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/3_request_for_flight_cancellation.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/3_request_for_flight_cancellation.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/3_request_for_flight_cancellation.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/4_flight_number_name.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/4_flight_number_name.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/4_flight_number_name.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/4_flight_number_name.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/5_refund_policy.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/5_refund_policy.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/5_refund_policy.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/5_refund_policy.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/6_flight_refunded.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/6_flight_refunded.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/6_flight_refunded.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/6_flight_refunded.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/realtime_agent_swarm.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/realtime_agent_swarm.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/realtime_agent_swarm.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/realtime_agent_swarm.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/img/twilio_endpoint_config.png b/website/docs/_blogs/2024-12-20-RealtimeAgent/img/twilio_endpoint_config.png similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/img/twilio_endpoint_config.png rename to website/docs/_blogs/2024-12-20-RealtimeAgent/img/twilio_endpoint_config.png diff --git a/website/_blogs/2024-12-20-RealtimeAgent/index.mdx b/website/docs/_blogs/2024-12-20-RealtimeAgent/index.mdx similarity index 100% rename from website/_blogs/2024-12-20-RealtimeAgent/index.mdx rename to website/docs/_blogs/2024-12-20-RealtimeAgent/index.mdx diff --git a/website/_blogs/2024-12-20-Reasoning-Update/img/mcts_example.png b/website/docs/_blogs/2024-12-20-Reasoning-Update/img/mcts_example.png similarity index 100% rename from website/_blogs/2024-12-20-Reasoning-Update/img/mcts_example.png rename to website/docs/_blogs/2024-12-20-Reasoning-Update/img/mcts_example.png diff --git a/website/_blogs/2024-12-20-Reasoning-Update/img/reasoningagent_1.png b/website/docs/_blogs/2024-12-20-Reasoning-Update/img/reasoningagent_1.png similarity index 100% rename from website/_blogs/2024-12-20-Reasoning-Update/img/reasoningagent_1.png rename to website/docs/_blogs/2024-12-20-Reasoning-Update/img/reasoningagent_1.png diff --git a/website/_blogs/2024-12-20-Reasoning-Update/index.mdx b/website/docs/_blogs/2024-12-20-Reasoning-Update/index.mdx similarity index 100% rename from website/_blogs/2024-12-20-Reasoning-Update/index.mdx rename to website/docs/_blogs/2024-12-20-Reasoning-Update/index.mdx diff --git a/website/_blogs/2024-12-20-Tools-interoperability/index.mdx b/website/docs/_blogs/2024-12-20-Tools-interoperability/index.mdx similarity index 100% rename from website/_blogs/2024-12-20-Tools-interoperability/index.mdx rename to website/docs/_blogs/2024-12-20-Tools-interoperability/index.mdx diff --git a/website/_blogs/2025-01-07-Tools-Dependency-Injection/index.mdx b/website/docs/_blogs/2025-01-07-Tools-Dependency-Injection/index.mdx similarity index 100% rename from website/_blogs/2025-01-07-Tools-Dependency-Injection/index.mdx rename to website/docs/_blogs/2025-01-07-Tools-Dependency-Injection/index.mdx diff --git a/website/_blogs/2025-01-08-RealtimeAgent-over-websocket/img/websocket_communication_diagram.png b/website/docs/_blogs/2025-01-08-RealtimeAgent-over-websocket/img/websocket_communication_diagram.png similarity index 100% rename from website/_blogs/2025-01-08-RealtimeAgent-over-websocket/img/websocket_communication_diagram.png rename to website/docs/_blogs/2025-01-08-RealtimeAgent-over-websocket/img/websocket_communication_diagram.png diff --git a/website/_blogs/2025-01-08-RealtimeAgent-over-websocket/index.mdx b/website/docs/_blogs/2025-01-08-RealtimeAgent-over-websocket/index.mdx similarity index 100% rename from website/_blogs/2025-01-08-RealtimeAgent-over-websocket/index.mdx rename to website/docs/_blogs/2025-01-08-RealtimeAgent-over-websocket/index.mdx diff --git a/website/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_communication_diagram.png b/website/docs/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_communication_diagram.png similarity index 100% rename from website/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_communication_diagram.png rename to website/docs/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_communication_diagram.png diff --git a/website/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_connection_diagram.png b/website/docs/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_connection_diagram.png similarity index 100% rename from website/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_connection_diagram.png rename to website/docs/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/img/webrtc_connection_diagram.png diff --git a/website/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/index.mdx b/website/docs/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/index.mdx similarity index 100% rename from website/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/index.mdx rename to website/docs/_blogs/2025-01-09-RealtimeAgent-over-WebRTC/index.mdx diff --git a/website/_blogs/2025-01-10-WebSockets/img/structured_messages_with_websockets.png b/website/docs/_blogs/2025-01-10-WebSockets/img/structured_messages_with_websockets.png similarity index 100% rename from website/_blogs/2025-01-10-WebSockets/img/structured_messages_with_websockets.png rename to website/docs/_blogs/2025-01-10-WebSockets/img/structured_messages_with_websockets.png diff --git a/website/_blogs/2025-01-10-WebSockets/index.mdx b/website/docs/_blogs/2025-01-10-WebSockets/index.mdx similarity index 100% rename from website/_blogs/2025-01-10-WebSockets/index.mdx rename to website/docs/_blogs/2025-01-10-WebSockets/index.mdx diff --git a/website/_blogs/2025-01-22-Tools-ChatContext-Dependency-Injection/index.mdx b/website/docs/_blogs/2025-01-22-Tools-ChatContext-Dependency-Injection/index.mdx similarity index 100% rename from website/_blogs/2025-01-22-Tools-ChatContext-Dependency-Injection/index.mdx rename to website/docs/_blogs/2025-01-22-Tools-ChatContext-Dependency-Injection/index.mdx diff --git a/website/_blogs/2025-01-29-RealtimeAgent-with-gemini/img/RealtimeAgent_gemini.png b/website/docs/_blogs/2025-01-29-RealtimeAgent-with-gemini/img/RealtimeAgent_gemini.png similarity index 100% rename from website/_blogs/2025-01-29-RealtimeAgent-with-gemini/img/RealtimeAgent_gemini.png rename to website/docs/_blogs/2025-01-29-RealtimeAgent-with-gemini/img/RealtimeAgent_gemini.png diff --git a/website/_blogs/2025-01-29-RealtimeAgent-with-gemini/index.mdx b/website/docs/_blogs/2025-01-29-RealtimeAgent-with-gemini/index.mdx similarity index 100% rename from website/_blogs/2025-01-29-RealtimeAgent-with-gemini/index.mdx rename to website/docs/_blogs/2025-01-29-RealtimeAgent-with-gemini/index.mdx diff --git a/website/_blogs/2025-01-31-WebSurferAgent/index.mdx b/website/docs/_blogs/2025-01-31-WebSurferAgent/index.mdx similarity index 100% rename from website/_blogs/2025-01-31-WebSurferAgent/index.mdx rename to website/docs/_blogs/2025-01-31-WebSurferAgent/index.mdx diff --git a/website/_blogs/2025-01-31-Websurfing-Tools/index.mdx b/website/docs/_blogs/2025-01-31-Websurfing-Tools/index.mdx similarity index 100% rename from website/_blogs/2025-01-31-Websurfing-Tools/index.mdx rename to website/docs/_blogs/2025-01-31-Websurfing-Tools/index.mdx diff --git a/website/_blogs/2025-02-05-Communication-Agents/img/commsagents_discord_msg.png b/website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_discord_msg.png similarity index 100% rename from website/_blogs/2025-02-05-Communication-Agents/img/commsagents_discord_msg.png rename to website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_discord_msg.png diff --git a/website/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_finalmsg.png b/website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_finalmsg.png similarity index 100% rename from website/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_finalmsg.png rename to website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_finalmsg.png diff --git a/website/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_msg.png b/website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_msg.png similarity index 100% rename from website/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_msg.png rename to website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_slack_msg.png diff --git a/website/_blogs/2025-02-05-Communication-Agents/img/commsagents_telegram_msg.png b/website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_telegram_msg.png similarity index 100% rename from website/_blogs/2025-02-05-Communication-Agents/img/commsagents_telegram_msg.png rename to website/docs/_blogs/2025-02-05-Communication-Agents/img/commsagents_telegram_msg.png diff --git a/website/_blogs/2025-02-05-Communication-Agents/index.mdx b/website/docs/_blogs/2025-02-05-Communication-Agents/index.mdx similarity index 100% rename from website/_blogs/2025-02-05-Communication-Agents/index.mdx rename to website/docs/_blogs/2025-02-05-Communication-Agents/index.mdx diff --git a/website/_blogs/2025-02-13-DeepResearchAgent/img/DeepResearchAgent.png b/website/docs/_blogs/2025-02-13-DeepResearchAgent/img/DeepResearchAgent.png similarity index 100% rename from website/_blogs/2025-02-13-DeepResearchAgent/img/DeepResearchAgent.png rename to website/docs/_blogs/2025-02-13-DeepResearchAgent/img/DeepResearchAgent.png diff --git a/website/_blogs/2025-02-13-DeepResearchAgent/index.mdx b/website/docs/_blogs/2025-02-13-DeepResearchAgent/index.mdx similarity index 100% rename from website/_blogs/2025-02-13-DeepResearchAgent/index.mdx rename to website/docs/_blogs/2025-02-13-DeepResearchAgent/index.mdx diff --git a/website/docs/faq/FAQ.mdx b/website/docs/faq/FAQ.mdx index e7e04f5e72..fefd64f51b 100644 --- a/website/docs/faq/FAQ.mdx +++ b/website/docs/faq/FAQ.mdx @@ -62,10 +62,10 @@ The `AssistantAgent` doesn't save all the code by default, because there are cas ## Legacy code executor -:::note + The new code executors offers more choices of execution backend. Read more about [code executors](/docs/tutorial/code-executors). -::: + The legacy code executor is used by specifying the `code_execution_config` in the agent's constructor. diff --git a/website/docs/user-guide/advanced-concepts/code-execution.mdx b/website/docs/user-guide/advanced-concepts/code-execution.mdx index 2a3f21db4e..4ccdd461b8 100644 --- a/website/docs/user-guide/advanced-concepts/code-execution.mdx +++ b/website/docs/user-guide/advanced-concepts/code-execution.mdx @@ -19,9 +19,9 @@ For each type of executor, AG2 provides two ways to execute code: locally and in The figure below shows the architecture of the local command line code executor ([`autogen.coding.LocalCommandLineCodeExecutor`](/docs/api-reference/autogen/coding/LocalCommandLineCodeExecutor)). -:::danger + Executing LLM-generated code poses a security risk to your host environment. -::: + ![Code Executor No Docker](./assets/code-executor-no-docker.png) diff --git a/website/docs/user-guide/models/vLLM.mdx b/website/docs/user-guide/models/vLLM.mdx index 9de5d7a3d6..2dd6c35cf2 100644 --- a/website/docs/user-guide/models/vLLM.mdx +++ b/website/docs/user-guide/models/vLLM.mdx @@ -1,6 +1,7 @@ --- title: vLLM sidebarTitle: vLLM +render_macros: false --- [vLLM](https://github.com/vllm-project/vllm) is a locally run proxy and inference server, providing an OpenAI-compatible API. As it performs both the proxy and the inferencing, you don't need to install an additional inference server. diff --git a/website/mkdocs/.gitignore b/website/mkdocs/.gitignore new file mode 100644 index 0000000000..c9490a5320 --- /dev/null +++ b/website/mkdocs/.gitignore @@ -0,0 +1 @@ +/site diff --git a/website/mkdocs/README.md b/website/mkdocs/README.md new file mode 100644 index 0000000000..10a94d5e53 --- /dev/null +++ b/website/mkdocs/README.md @@ -0,0 +1,3 @@ +# :warning: DOCUMENTATION CODE SOURCES :warning: + +To find a real docs, just visit our website: [https://faststream.airt.ai/latest/](https://faststream.airt.ai/latest/) diff --git a/website/mkdocs/create_api_docs.py b/website/mkdocs/create_api_docs.py new file mode 100644 index 0000000000..f635aeceaa --- /dev/null +++ b/website/mkdocs/create_api_docs.py @@ -0,0 +1,312 @@ +"""Create API documentation for a module.""" + +import itertools +import shutil +from importlib import import_module +from inspect import getmembers, isclass, isfunction +from pathlib import Path +from pkgutil import walk_packages +from types import FunctionType, ModuleType +from typing import Any, List, Optional, Tuple, Type, Union + +API_META = "# 0.5 - API\n# 2 - Release\n# 3 - Contributing\n# 5 - Template Page\n# 10 - Default\nsearch:\n boost: 0.5" + +MD_API_META = "---\n" + API_META + "\n---\n\n" + + +PUBLIC_API_FILES = [ + "faststream/opentelemetry/__init__.py", + "faststream/asgi/__init__.py", + "faststream/asyncapi/__init__.py", + "faststream/__init__.py", + "faststream/nats/__init__.py", + "faststream/rabbit/__init__.py", + "faststream/confluent/__init__.py", + "faststream/kafka/__init__.py", + "faststream/redis/__init__.py", +] + + +def _get_submodules(package_name: str) -> List[str]: + """Get all submodules of a package. + + Args: + package_name: The name of the package. + + Returns: + A list of submodules. + + !!! note + + The above docstring is autogenerated by docstring-gen library (https://github.com/airtai/docstring-gen) + """ + try: + # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import + m = import_module(package_name) + except ModuleNotFoundError as e: + raise e + submodules = [info.name for info in walk_packages(m.__path__, prefix=f"{package_name}.")] + submodules = [x for x in submodules if not any(name.startswith("_") for name in x.split("."))] + return [package_name, *submodules] + + +def _import_submodules(module_name: str, include_public_api_only: bool = False) -> Optional[List[ModuleType]]: + def _import_module(name: str) -> Optional[ModuleType]: + try: + # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import + return import_module(name) + except Exception: + return None + + package_names = _get_submodules(module_name) + modules = [_import_module(n) for n in package_names] + if include_public_api_only: + repo_path = Path.cwd().parent + + # Extract only faststream/__init__.py or faststream//__init__.py + public_api_modules = [ + m for m in modules if m and m.__file__.replace(str(repo_path) + "/", "") in PUBLIC_API_FILES + ] + + return public_api_modules + return [m for m in modules if m is not None] + + +def _import_functions_and_classes( + m: ModuleType, include_public_api_only: bool = False +) -> List[Tuple[str, Union[FunctionType, Type[Any]]]]: + funcs_and_classes = [] + if not include_public_api_only: + funcs_and_classes = [(x, y) for x, y in getmembers(m) if isfunction(y) or isclass(y)] + + if hasattr(m, "__all__"): + for t in m.__all__: + obj = getattr(m, t) + if isfunction(obj) or isclass(obj): + funcs_and_classes.append((t, m.__name__ + "." + t)) + + return funcs_and_classes + + +def _is_private(name: str) -> bool: + parts = name.split(".") + return any(part.startswith("_") for part in parts) + + +def _import_all_members(module_name: str, include_public_api_only: bool = False) -> List[str]: + submodules = _import_submodules(module_name, include_public_api_only=include_public_api_only) + members: List[Tuple[str, Union[FunctionType, Type[Any]]]] = list( + itertools.chain(*[ + _import_functions_and_classes(m, include_public_api_only=include_public_api_only) for m in submodules + ]) + ) + + names = [y if isinstance(y, str) else f"{y.__module__}.{y.__name__}" for x, y in members] + names = [name for name in names if not _is_private(name) and name.startswith(module_name)] + return names + + +def _merge_lists(members: List[str], submodules: List[str]) -> List[str]: + members_copy = members[:] + for sm in submodules: + for i, el in enumerate(members_copy): + if el.startswith(sm): + members_copy.insert(i, sm) + break + return members_copy + + +def _add_all_submodules(members: List[str]) -> List[str]: + def _f(x: str) -> List[str]: + xs = x.split(".") + return [".".join(xs[:i]) + "." for i in range(1, len(xs))] + + def _get_sorting_key(item): + y = item.split(".") + z = [f"~{a}" for a in y[:-1]] + [y[-1]] + return ".".join(z) + + submodules = list(set(itertools.chain(*[_f(x) for x in members]))) + members = _merge_lists(members, submodules) + members = list(dict.fromkeys(members)) + return sorted(members, key=_get_sorting_key) + + +def _get_api_summary_item(x: str) -> str: + xs = x.split(".") + if x.endswith("."): + indent = " " * (4 * (len(xs) - 1 + 1)) + return f"{indent}- {xs[-2]}" + else: + indent = " " * (4 * (len(xs) + 1)) + return f"{indent}- [{xs[-1]}](api/{'/'.join(xs)}.md)" + + +def _get_api_summary(members: List[str]) -> str: + return "\n".join([_get_api_summary_item(x) for x in members]) + + +def _generate_api_doc(name: str, docs_path: Path) -> Path: + xs = name.split(".") + module_name = ".".join(xs[:-1]) + member_name = xs[-1] + path = docs_path / f"{('/').join(xs)}.md" + content = f"::: {module_name}.{member_name}\n" + + path.parent.mkdir(exist_ok=True, parents=True) + path.write_text(MD_API_META + content) + + return path + + +def _generate_api_docs(members: List[str], docs_path: Path) -> List[Path]: + return [_generate_api_doc(x, docs_path) for x in members if not x.endswith(".")] + + +def _get_submodule_members(module_name: str) -> List[str]: + """Get a list of all submodules contained within the module. + + Args: + module_name: The name of the module to retrieve submodules from + + Returns: + A list of submodule names within the module + """ + members = _import_all_members(module_name) + members_with_submodules = _add_all_submodules(members) + members_with_submodules_str: List[str] = [x[:-1] if x.endswith(".") else x for x in members_with_submodules] + return members_with_submodules_str + + +def _load_submodules( + module_name: str, + members_with_submodules: List[str], +) -> List[Union[FunctionType, Type[Any]]]: + """Load the given submodules from the module. + + Args: + module_name: The name of the module whose submodules to load + members_with_submodules: A list of submodule names to load + + Returns: + A list of imported submodule objects. + """ + submodules = _import_submodules(module_name) + members = itertools.chain(*map(_import_functions_and_classes, submodules)) + names = [ + y + for _, y in members + if (isinstance(y, str) and y in members_with_submodules) + or (f"{y.__module__}.{y.__name__}" in members_with_submodules) + ] + return names + + +def _update_single_api_doc(symbol: Union[FunctionType, Type[Any]], docs_path: Path, module_name: str) -> None: + en_docs_path = docs_path / "docs" / "en" + + if isinstance(symbol, str): + class_name = symbol.split(".")[-1] + module_name = ".".join(symbol.split(".")[:-1]) + # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import + obj = getattr(import_module(module_name), class_name) + if obj.__module__.startswith(module_name): + obj = symbol + filename = symbol + + else: + obj = symbol + filename = f"{symbol.__module__}.{symbol.__name__}" + + content = "::: %s\n" % (obj if isinstance(obj, str) else f"{obj.__module__}.{obj.__qualname__}") + + target_file_path = "/".join(filename.split(".")) + ".md" + + (en_docs_path / "api" / target_file_path).write_text(MD_API_META + content) + + +def _update_api_docs(symbols: List[Union[FunctionType, Type[Any]]], docs_path: Path, module_name: str) -> None: + for symbol in symbols: + _update_single_api_doc(symbol=symbol, docs_path=docs_path, module_name=module_name) + + +def _generate_api_docs_for_module(root_path: Path, module_name: str) -> Tuple[str, str]: + """Generate API documentation for a module. + + Args: + root_path: The root path of the project. + module_name: The name of the module. + + Returns: + A string containing the API documentation for the module. + + """ + public_api_summary = _get_api_summary( + _add_all_submodules(_import_all_members(module_name, include_public_api_only=True)) + ) + # Using public_api/ symlink pointing to api/ because of the issue + # https://github.com/mkdocs/mkdocs/issues/1974 + public_api_summary = public_api_summary.replace("(api/", "(public_api/") + + members = _import_all_members(module_name) + members_with_submodules = _add_all_submodules(members) + api_summary = _get_api_summary(members_with_submodules) + + api_root = root_path / "docs" / "en" / "api" + shutil.rmtree(api_root / module_name, ignore_errors=True) + api_root.mkdir(parents=True, exist_ok=True) + + (api_root / ".meta.yml").write_text(API_META) + + _generate_api_docs(members_with_submodules, api_root) + + members_with_submodules = _get_submodule_members(module_name) + symbols = _load_submodules(module_name, members_with_submodules) + + _update_api_docs(symbols, root_path, module_name) + + # todo: fix the problem and remove this + src = """ - [ContactDict](api/faststream/asyncapi/schema/info/ContactDict.md) +""" + dst = """ - [ContactDict](api/faststream/asyncapi/schema/info/ContactDict.md) + - [EmailStr](api/faststream/asyncapi/schema/info/EmailStr.md) +""" + api_summary = api_summary.replace(src, dst) + + return api_summary, public_api_summary + + +def create_api_docs( + root_path: Path, + module: str, +) -> None: + """Generate API documentation for a module. + + Args: + root_path: The root path of the project. + module: The name of the module. + + """ + api, public_api = _generate_api_docs_for_module(root_path, module) + + docs_dir = root_path / "docs" + + # read summary template from file + navigation_template = (docs_dir / "navigation_template.txt").read_text() + + summary = navigation_template.format(api=api, public_api=public_api) + + summary = "\n".join(filter(bool, (x.rstrip() for x in summary.split("\n")))) + + (docs_dir / "SUMMARY.md").write_text(summary) + + +def on_page_markdown(markdown, *, page, config, files): + """Mkdocs hook to update the edit URL for the public API pages.""" + if page.edit_url and "public_api" in page.edit_url: + page.edit_url = page.edit_url.replace("public_api", "api") + + +if __name__ == "__main__": + root = Path(__file__).resolve().parent + create_api_docs(root, "faststream") diff --git a/website/mkdocs/data/people.yml b/website/mkdocs/data/people.yml new file mode 100644 index 0000000000..e7d356a7a5 --- /dev/null +++ b/website/mkdocs/data/people.yml @@ -0,0 +1,7 @@ +people: + - name: "Davor Runje" + username: davorrunje + github: https://github.com/davorrunje + avatar: https://avatars.githubusercontent.com/u/24715380?v=4 + include: + - team diff --git a/website/mkdocs/docs.py b/website/mkdocs/docs.py new file mode 100644 index 0000000000..22a4770ece --- /dev/null +++ b/website/mkdocs/docs.py @@ -0,0 +1,229 @@ +"""A script to help with the translation of the docs.""" + +import os +import subprocess +from http.server import HTTPServer, SimpleHTTPRequestHandler +from pathlib import Path +from shutil import rmtree +from typing import Optional + +import mkdocs.commands.build +import mkdocs.commands.serve +import typer +from create_api_docs import create_api_docs +from mkdocs.config import load_config +from typing_extensions import Annotated + +from autogen._website.generate_mkdocs import main as generate_files_for_mkdocs + +IGNORE_DIRS = ("assets", "stylesheets", "javascripts") + +BASE_DIR = Path(__file__).resolve().parent +CONFIG = BASE_DIR / "mkdocs.yml" +DOCS_DIR = BASE_DIR / "docs" +LANGUAGES_DIRS = tuple(filter(lambda f: f.is_dir() and f.name not in IGNORE_DIRS, DOCS_DIR.iterdir())) +BUILD_DIR = BASE_DIR / "site" + +# EN_DOCS_DIR = DOCS_DIR / "en" +# EN_INDEX_PATH = EN_DOCS_DIR / "index.md" +# README_PATH = BASE_DIR.parent / "README.md" +# EN_CONTRIBUTING_PATH = ( +# EN_DOCS_DIR / "getting-started" / "contributing" / "CONTRIBUTING.md" +# ) +# CONTRIBUTING_PATH = BASE_DIR.parent / "CONTRIBUTING.md" + + +config = load_config(str(CONFIG)) + +DEV_SERVER = str(config.get("dev_addr", "0.0.0.0:3000")) + + +def get_missing_translation(lng: str) -> Path: + return DOCS_DIR / lng / "helpful" / "missing-translation.md" + + +def get_in_progress(lng: str) -> Path: + return DOCS_DIR / lng / "helpful" / "in-progress.md" + + +app = typer.Typer() + + +def get_default_title(file: Path) -> str: + title = file.stem.upper().replace("-", " ") + if title == "INDEX": + title = get_default_title(file.parent) + return title + + +def join_nested(root: Path, path: str) -> Path: + for i in path.split("/"): + root = root / i + return _touch_file(root) + + +def _touch_file(path: Path) -> Path: + if not path.suffixes: + path.mkdir(parents=True, exist_ok=True) + else: + path.parent.mkdir(parents=True, exist_ok=True) + return path + + +@app.command() +def preview(): + """A quick server to preview a built site. + + For development, prefer the command live (or just mkdocs serve). + This is here only to preview a built site. + """ + _build() + typer.echo("Warning: this is a very simple server.") + typer.echo("For development, use the command live instead.") + typer.echo("This is here only to preview a built site.") + os.chdir(BUILD_DIR) + addr, port = DEV_SERVER.split(":") + server = HTTPServer((addr, int(port)), SimpleHTTPRequestHandler) + typer.echo(f"Serving at: http://{DEV_SERVER}") + server.serve_forever() + + +@app.command() +def live(port: Annotated[Optional[str], typer.Argument()] = None): + dev_server = f"0.0.0.0:{port}" if port else DEV_SERVER + + typer.echo("Serving mkdocs with live reload") + typer.echo(f"Serving at: http://{dev_server}") + mkdocs.commands.serve.serve(dev_addr=dev_server) + + +@app.command() +def build(): + _build() + + +@app.command() +def add(path=typer.Argument(...)): + title = "" + + exists = [] + not_exists = [] + + for i in LANGUAGES_DIRS: + file = join_nested(i, path) + + if file.exists(): + exists.append(i) + + if not title: + with file.open("r") as r: + title = r.readline() + + else: + not_exists.append(i) + file.write_text(f"# {title or get_default_title(file)} \n{{! " + get_in_progress(i.name) + " !}") + typer.echo(f"{file} - write `in progress`") + + if len(exists): + for i in not_exists: + file = i / path + file.write_text(f"# {title or get_default_title(file)} \n{{! " + get_missing_translation(i.name) + " !}") + typer.echo(f"{file} - write `missing translation`") + + +@app.command() +def rm(path: str = typer.Argument(...)): + delete = typer.confirm("Are you sure you want to delete files?") + if not delete: + typer.echo("Not deleting") + raise typer.Abort() + + for i in LANGUAGES_DIRS: + file = i / path + if file.exists(): + if file.is_dir(): + rmtree(file) + else: + file.unlink() + typer.echo(f"{file} removed") + + if file.parent.exists() and not tuple(file.parent.iterdir()): + file.parent.rmdir() + typer.echo(f"{file.parent} removed") + + +@app.command() +def mv(path: str = typer.Argument(...), new_path: str = typer.Argument(...)): + for i in LANGUAGES_DIRS: + file = i / path + if file.exists(): + file.rename(i / new_path) + typer.echo(f"{i / new_path} moved") + + +@app.command() +def update_readme() -> None: + """Update README.md by expanding embeddings in docs/docs/en/index.md.""" + # todo: fix this function + typer.echo("Skipping updating README.md for now") + return None + + # typer.echo(f"Updating README.md") + # expand_markdown(input_markdown_path=EN_INDEX_PATH, output_markdown_path=README_PATH) + + # remove_lines_between_dashes(file_path=README_PATH) + + # relative_path = os.path.relpath(EN_INDEX_PATH, BASE_DIR.parent) + # auto_generated = f"[Note]: # (This is an auto-generated file. Please edit {relative_path} instead)\n\n" + + # existing_content = open(README_PATH).read() + # open(README_PATH, "w").write(auto_generated + existing_content) + + +# @app.command() +# def update_contributing(): +# """Update CONTRIBUTING.md by expanding embeddings in docs/docs/en/CONTRIBUTING.md.""" +# typer.echo("Updating CONTRIBUTING.md") +# expand_markdown( +# input_markdown_path=EN_CONTRIBUTING_PATH, +# output_markdown_path=CONTRIBUTING_PATH, +# ) + +# existing_content = CONTRIBUTING_PATH.read_text() + +# _, content = find_metablock(existing_content.splitlines()) + +# relative_path = EN_CONTRIBUTING_PATH.relative_to(BASE_DIR.parent) + +# CONTRIBUTING_PATH.write_text( +# "\n".join( +# ( +# f"> **_NOTE:_** This is an auto-generated file. Please edit {relative_path} instead.", +# *content, +# ) +# ) +# + "\n" +# ) + + +@app.command() +def build_api_docs(): + """Build api docs for autogen.""" + typer.echo("Updating API docs") + create_api_docs(root_path=BASE_DIR, module="autogen") + + +def _build(): + # build_api_docs() + # update_readme() + # update_contributing() + + # typer.echo("Updating Release Notes") + # update_release_notes(realease_notes_path=EN_DOCS_DIR / "release.md") + + generate_files_for_mkdocs() + subprocess.run(["mkdocs", "build", "--site-dir", BUILD_DIR], check=True) + + +if __name__ == "__main__": + app() diff --git a/website/mkdocs/docs/.gitignore b/website/mkdocs/docs/.gitignore new file mode 100644 index 0000000000..3242404b35 --- /dev/null +++ b/website/mkdocs/docs/.gitignore @@ -0,0 +1 @@ +SUMMARY.md diff --git a/website/mkdocs/docs/assets/img/ag2.svg b/website/mkdocs/docs/assets/img/ag2.svg new file mode 100644 index 0000000000..c6d1ce5656 --- /dev/null +++ b/website/mkdocs/docs/assets/img/ag2.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/website/mkdocs/docs/assets/img/autogen_app.svg b/website/mkdocs/docs/assets/img/autogen_app.svg new file mode 100644 index 0000000000..0799dfdb03 --- /dev/null +++ b/website/mkdocs/docs/assets/img/autogen_app.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/mkdocs/docs/assets/img/conv_2.svg b/website/mkdocs/docs/assets/img/conv_2.svg new file mode 100644 index 0000000000..a4b60cafe9 --- /dev/null +++ b/website/mkdocs/docs/assets/img/conv_2.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/mkdocs/docs/assets/img/extend.svg b/website/mkdocs/docs/assets/img/extend.svg new file mode 100644 index 0000000000..524ddeb8fc --- /dev/null +++ b/website/mkdocs/docs/assets/img/extend.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/mkdocs/docs/assets/img/favicon.ico b/website/mkdocs/docs/assets/img/favicon.ico new file mode 100644 index 0000000000..6a71ddeaaa Binary files /dev/null and b/website/mkdocs/docs/assets/img/favicon.ico differ diff --git a/website/mkdocs/docs/assets/img/homepage-hero-background-large.png b/website/mkdocs/docs/assets/img/homepage-hero-background-large.png new file mode 100644 index 0000000000..32c2fd88ee --- /dev/null +++ b/website/mkdocs/docs/assets/img/homepage-hero-background-large.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db32074ba93362a494020ee7fb2d35694468ebb0c9a9711554d361140beed378 +size 432690 diff --git a/website/mkdocs/docs/assets/img/homepage-hero-background.png b/website/mkdocs/docs/assets/img/homepage-hero-background.png new file mode 100644 index 0000000000..2b5d6ca63c --- /dev/null +++ b/website/mkdocs/docs/assets/img/homepage-hero-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb36cbba178916d0a3f26b737e374e23c5245c83c2c36548dd7d0db7387401a1 +size 210083 diff --git a/website/mkdocs/docs/assets/img/logo.svg b/website/mkdocs/docs/assets/img/logo.svg new file mode 100644 index 0000000000..cea304d77d --- /dev/null +++ b/website/mkdocs/docs/assets/img/logo.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/website/mkdocs/docs/docs/.gitignore b/website/mkdocs/docs/docs/.gitignore new file mode 100644 index 0000000000..44b00ba028 --- /dev/null +++ b/website/mkdocs/docs/docs/.gitignore @@ -0,0 +1,3 @@ +/* +!/home/ +!.gitignore diff --git a/website/mkdocs/docs/docs/home/home.md b/website/mkdocs/docs/docs/home/home.md new file mode 100644 index 0000000000..702c8eee5e --- /dev/null +++ b/website/mkdocs/docs/docs/home/home.md @@ -0,0 +1,144 @@ +--- +title: "Key Features" +--- + +
+
+
+ +
+
+

AG2

+

The Open Source Agent OS

+ +
Getting Started - 3 Minute
+
+
+
+
+ +### Key Features + +
+
+
+ Multi-Agent Conversation Framework +

Multi-Agent Conversation Framework

+

AG2 provides multi-agent conversation framework as a high-level abstraction. With this framework, one can conveniently build LLM workflows.

+
+
+ +
+
+ Easily Build Diverse Applications +

Easily Build Diverse Applications

+

AG2 offers a collection of working systems spanning a wide range of applications from various domains and complexities.

+
+
+ +
+
+ Enhanced LLM Inference & Optimization +

Enhanced LLM Inference & Optimization

+

AG2 supports enhanced LLM inference APIs, which can be used to improve inference performance and reduce cost.

+
+
+
+ +### Explore content +
+ +
+ +### Popular resources + diff --git a/website/mkdocs/docs/docs/home/quick-start.md b/website/mkdocs/docs/docs/home/quick-start.md new file mode 100644 index 0000000000..9f0708035b --- /dev/null +++ b/website/mkdocs/docs/docs/home/quick-start.md @@ -0,0 +1,179 @@ +--- +title: "Quick Start" +--- + +AG2 (formerly AutoGen) is an open-source programming framework for building AI agents and facilitating their cooperation to solve tasks. AG2 supports tool use, autonomous and human-in-the-loop workflows, and multi-agent conversation patterns. + +### Let's go + +```sh +pip install ag2 +``` +!!! note + If you have been using `autogen` or `pyautogen`, all you need to do is upgrade it using: + ```bash + pip install -U autogen + ``` + or + ```bash + pip install -U pyautogen + ``` + as `pyautogen`, `autogen`, and `ag2` are aliases for the same PyPI package. + +=== "Chat with an agent" + + ```python + # 1. Import our agent class + from autogen import ConversableAgent + + # 2. Define our LLM configuration for OpenAI's GPT-4o mini + # uses the OPENAI_API_KEY environment variable + llm_config = {"api_type": "openai", "model": "gpt-4o-mini"} + + # 3. Create our LLM agent + my_agent = ConversableAgent( + name="helpful_agent", + llm_config=llm_config, + system_message="You are a poetic AI assistant, respond in rhyme.", + ) + + # 4. Run the agent with a prompt + chat_result = my_agent.run("In one sentence, what's the big deal about AI?") + + # 5. Print the chat + print(chat_result.chat_history) + ``` + +=== "Two agent chat" + + ```python + # Chat between two comedian agents + + # 1. Import our agent class + from autogen import ConversableAgent + + # 2. Define our LLM configuration for OpenAI's GPT-4o mini, + # uses the OPENAI_API_KEY environment variable + llm_config = {"api_type": "openai", "model": "gpt-4o-mini"} + + # 3. Create our agents who will tell each other jokes, + # with Jack ending the chat when Emma says FINISH + jack = ConversableAgent( + "Jack", + llm_config=llm_config, + system_message=( + "Your name is Jack and you are a comedian " + "in a two-person comedy show." + ), + is_termination_msg=lambda x: True if "FINISH" in x["content"] else False + ) + emma = ConversableAgent( + "Emma", + llm_config=llm_config, + system_message=( + "Your name is Emma and you are a comedian " + "in a two-person comedy show. Say the word FINISH " + "ONLY AFTER you've heard 2 of Jack's jokes." + ), + ) + + # 4. Run the chat + chat_result = jack.initiate_chat( + emma, + message="Emma, tell me a joke about goldfish and peanut butter.", + ) + + # 5. Print the chat + print(chat_result.chat_history) + ``` + +=== "Group chat" + + ```python + # Group chat amongst agents to create a 4th grade lesson plan + # Flow determined by Group Chat Manager automatically, and + # should be Teacher > Planner > Reviewer > Teacher (repeats if necessary) + + # 1. Import our agent and group chat classes + from autogen import ConversableAgent, GroupChat, GroupChatManager + + # Define our LLM configuration for OpenAI's GPT-4o mini + # uses the OPENAI_API_KEY environment variable + llm_config = {"api_type": "openai", "model": "gpt-4o-mini"} + + # Planner agent setup + planner_message = "Create lesson plans for 4th grade. Use format: , <learning_objectives>, <script>" + planner = ConversableAgent( + name="planner_agent", + llm_config=llm_config, + system_message=planner_message, + description="Creates lesson plans" + ) + + # Reviewer agent setup + reviewer_message = "Review lesson plans against 4th grade curriculum. Provide max 3 changes." + reviewer = ConversableAgent( + name="reviewer_agent", + llm_config=llm_config, + system_message=reviewer_message, + description="Reviews lesson plans" + ) + + # Teacher agent setup + teacher_message = "Choose topics and work with planner and reviewer. Say DONE! when finished." + teacher = ConversableAgent( + name="teacher_agent", + llm_config=llm_config, + system_message=teacher_message, + ) + + # Setup group chat + groupchat = GroupChat( + agents=[teacher, planner, reviewer], + speaker_selection_method="auto", + messages=[] + ) + + # Create manager + # At each turn, the manager will check if the message contains DONE! and end the chat if so + # Otherwise, it will select the next appropriate agent using its LLM + manager = GroupChatManager( + name="group_manager", + groupchat=groupchat, + llm_config=llm_config, + is_termination_msg=lambda x: "DONE!" in (x.get("content", "") or "").upper() + ) + + # Start the conversation + chat_result = teacher.initiate_chat( + recipient=manager, + message="Let's teach the kids about the solar system." + ) + + # Print the chat + print(chat_result.chat_history) + ``` + +!!! tip + Learn more about configuring LLMs for agents + [here](/docs/user-guide/basic-concepts/llm-configuration). + + +### Where to Go Next? + +- Go through the [basic concepts](/docs/user-guide/basic-concepts/installing-ag2) to get started +- Once you're ready, hit the [advanced concepts](/docs/user-guide/advanced-concepts/rag) +- Explore the [API Reference](/docs/api-reference/autogen/overview) +- Chat on [Discord](https://discord.gg/pAbnFJrkgZ) +- Follow on [X](https://x.com/ag2oss) + +If you like our project, please give it a [star](https://github.com/ag2ai/ag2) on GitHub. If you are interested in contributing, please read [Contributor's Guide](/contributor-guide/contributing). + +<iframe + src="https://ghbtns.com/github-btn.html?user=ag2ai&repo=ag2&type=star&count=true&size=large" + frameborder="0" + scrolling="0" + width="170" + height="30" + title="GitHub" +></iframe> diff --git a/website/mkdocs/docs/index.md b/website/mkdocs/docs/index.md new file mode 100644 index 0000000000..3905e8f0e1 --- /dev/null +++ b/website/mkdocs/docs/index.md @@ -0,0 +1,4 @@ +--- +title: ag2 +template: home.html +--- diff --git a/website/mkdocs/docs/javascripts/extra.js b/website/mkdocs/docs/javascripts/extra.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/mkdocs/docs/navigation_template.txt b/website/mkdocs/docs/navigation_template.txt new file mode 100644 index 0000000000..fe08d4afe6 --- /dev/null +++ b/website/mkdocs/docs/navigation_template.txt @@ -0,0 +1,84 @@ +--- +search: + exclude: true +--- +- Home + - [Home](docs/home/home.md) + - [Quick Start](docs/home/quick-start.md) +- User Guide + - Basic Concepts + - [Installing AG2](docs/user-guide/basic-concepts/installing-ag2.md) + - LLM Configuration + - [LLM Configuration](docs/user-guide/basic-concepts/llm-configuration/llm-configuration.md) + - [Structured Outputs](docs/user-guide/basic-concepts/llm-configuration/structured-outputs.md) + - [Conversable Agent](docs/user-guide/basic-concepts/conversable-agent.md) + - [Human In The Loop](docs/user-guide/basic-concepts/human-in-the-loop.md) + - Orchestrating Agents + - [Orchestrations](docs/user-guide/basic-concepts/orchestration/orchestrations.md) + - [Sequential Chat](docs/user-guide/basic-concepts/orchestration/sequential-chat.md) + - [Nested Chat](docs/user-guide/basic-concepts/orchestration/nested-chat.md) + - [Swarm](docs/user-guide/basic-concepts/orchestration/swarm.md) + - [Ending A Chat](docs/user-guide/basic-concepts/orchestration/ending-a-chat.md) + - [Tools](docs/user-guide/basic-concepts/tools.md) + - Advanced Concepts + - [RAG](docs/user-guide/advanced-concepts/rag.md) + - GroupChat + - [Groupchat](docs/user-guide/advanced-concepts/groupchat/groupchat.md) + - [Custom Group Chat](docs/user-guide/advanced-concepts/groupchat/custom-group-chat.md) + - [Resuming Group Chat](docs/user-guide/advanced-concepts/groupchat/resuming-group-chat.md) + - Swarm + - [Deep Dive](docs/user-guide/advanced-concepts/swarm/deep-dive.md) + - [Nested Chat](docs/user-guide/advanced-concepts/swarm/nested-chat.md) + - [Concept Code](docs/user-guide/advanced-concepts/swarm/concept-code.md) + - [Use Case](docs/user-guide/advanced-concepts/swarm/use-case.md) + - [Code Execution](docs/user-guide/advanced-concepts/code-execution.md) + - [Tools With Secrets](docs/user-guide/advanced-concepts/tools-with-secrets.md) + - [Conversation Patterns Deep Dive](docs/user-guide/advanced-concepts/conversation-patterns-deep-dive.md) + - [LLM Configuration Deep Dive](docs/user-guide/advanced-concepts/llm-configuration-deep-dive.md) + - Model Providers + - [Openai](docs/user-guide/models/openai.md) + - [Amazon Bedrock](docs/user-guide/models/amazon-bedrock.md) + - [Anthropic](docs/user-guide/models/anthropic.md) + - [Cerebras](docs/user-guide/models/cerebras.md) + - [Cohere](docs/user-guide/models/cohere.md) + - [Deepseek V3](docs/user-guide/models/deepseek-v3.md) + - [Google Gemini](docs/user-guide/models/google-gemini.md) + - [Google Vertexai](docs/user-guide/models/google-vertexai.md) + - [Groq](docs/user-guide/models/groq.md) + - [Litellm With Watsonx](docs/user-guide/models/litellm-with-watsonx.md) + - [Lm Studio](docs/user-guide/models/lm-studio.md) + - [Mistralai](docs/user-guide/models/mistralai.md) + - [Ollama](docs/user-guide/models/ollama.md) + - [Togetherai](docs/user-guide/models/togetherai.md) + - [Vllm](docs/user-guide/models/vLLM.md) + - Reference Agents + - [Index](docs/user-guide/reference-agents/index.md) + - [Captainagent](docs/user-guide/reference-agents/captainagent.md) + - [Communication Agents](docs/user-guide/reference-agents/communication-agents.md) + - [Docagent](docs/user-guide/reference-agents/docagent.md) + - [Reasoningagent](docs/user-guide/reference-agents/reasoningagent.md) + - [Websurferagent](docs/user-guide/reference-agents/websurferagent.md) +- Contributor Guide + - [Contributing](docs/contributor-guide/contributing.md) + - [Setup Development Environment](docs/contributor-guide/setup-development-environment.md) + - [Documentation](docs/contributor-guide/documentation.md) + - [File Bug Report](docs/contributor-guide/file-bug-report.md) + - [Maintainer](docs/contributor-guide/maintainer.md) + - [Pre Commit](docs/contributor-guide/pre-commit.md) + - [Tests](docs/contributor-guide/tests.md) + - [Research](docs/contributor-guide/Research.md) +- FAQs + - [Faq](docs/faq/FAQ.md) +- Ecosystem + - [Agentops](docs/ecosystem/agentops.md) + - [Azure_Cosmos_Db](docs/ecosystem/azure_cosmos_db.md) + - [Composio](docs/ecosystem/composio.md) + - [Databricks](docs/ecosystem/databricks.md) + - [Llamaindex](docs/ecosystem/llamaindex.md) + - [Mem0](docs/ecosystem/mem0.md) + - [Memgpt](docs/ecosystem/memgpt.md) + - [Microsoft Fabric](docs/ecosystem/microsoft-fabric.md) + - [Ollama](docs/ecosystem/ollama.md) + - [Pgvector](docs/ecosystem/pgvector.md) + - [Portkey](docs/ecosystem/portkey.md) + - [Promptflow](docs/ecosystem/promptflow.md) diff --git a/website/mkdocs/docs/stylesheets/extra.css b/website/mkdocs/docs/stylesheets/extra.css new file mode 100644 index 0000000000..3c13bbf6c1 --- /dev/null +++ b/website/mkdocs/docs/stylesheets/extra.css @@ -0,0 +1,369 @@ +/* +all variables +https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/main/_colors.scss +*/ + +/* Load Jersey 10 font*/ +@import url('https://fonts.googleapis.com/css2?family=Jersey+10&display=swap'); + +:root { + --md-primary-fg-color: #4b9cd6; + --md-primary-bg-color: #FFF; + --md-primary-fg-color--light: #4b9cd6; + --md-primary-fg-color--dark: #4b9cd6; +} + +[data-md-color-accent=indigo] { + --md-accent-fg-color: #4b9cd6; + --md-accent-fg-color--transparent: #4b9cd61A; + --md-typeset-a-color: #4b9cd6; +} + +[data-md-color-scheme=slate] { + --md-default-bg-color: hsla(var(--md-hue), 7%, 18%, 1); + --md-footer-bg-color--dark: hsla(var(--md-hue), 24%, 26%, 1); + --md-typeset-a-color: #4b9cd6; +} + +.user-link::before { + content: " "; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.user-list-center { + justify-content: space-evenly; + display: flex; + flex-wrap: wrap; +} + +.user { + flex: 1 0 20%; + margin: 1em; + min-width: 7em; +} + +.user .avatar-wrapper { + width: 80px; + height: 80px; + margin: 10px auto; + overflow: hidden; + border-radius: 50%; + position: relative; +} + +.user .avatar-wrapper img { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.user .title { + text-align: center; +} + +.user .count { + font-size: 80%; + text-align: center; +} + +a.external-link { + /* For right to left languages */ + direction: ltr; + display: inline-block; +} + +a.external-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0[↪]"; +} + +a.internal-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0↪"; +} + +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4, +.md-typeset h5, +.md-typeset h6 { + letter-spacing: 0.01em; + font-family: 'Jersey 10', sans-serif; + text-transform: uppercase; + font-weight: 400; +} + +.md-banner__inner strong.primary-text { + color: var(--md-primary-fg-color); +} + +/* Home page hero section starts */ +.homepage-hero-section { + background-image: url('/ag2/assets/img/homepage-hero-background.png'); + background-repeat: no-repeat; + background-size: contain; + background-position: center; + width: 100%; + height: auto; + aspect-ratio: 3.078; +} + +.hero-content { + display: flex; + flex-direction: row; + align-items: center; + gap: 5px; + max-width: 1200px; + width: 100%; + height: 100%; +} + +.hero-logo-section { + flex: 0 0 auto; +} + +.hero-logo { + width: 200px; + height: auto; +} + +.hero-text-section { + flex: 1; +} + +.md-typeset h2.hero-title { + font-size: 1.5rem; + line-height: 1.5rem; + margin: 0px; +} + +.md-typeset h2.hero-subtitle { + /* font-size: 1rem; */ + line-height: 1.75rem; + color: #3E4342; + margin: 0; +} + +p.hero-subtitle { + margin: 0 0 1.25em; +} + +.md-typeset a.hero-btn { + display: inline-block; + position: relative; + overflow: hidden; + background-color: rgb(0, 0, 0); + color: #fff; + padding: 7px 10px; + text-decoration: none; + font-weight: 500; + font-size: 0.85rem; + line-height: 100%; + box-sizing: border-box; + border: 2px solid rgb(255, 255, 255); + box-shadow: rgba(0, 0, 0, 0.16) 4px 4px 0px 0px; + --font-family-headings-custom: "Jersey 10" !important; + font-family: var( + --font-family-headings-custom, + var(--font-inter, sans-serif) +); + transition: 200ms all ease; +} + +.hero-btn::before{ + content: ''; + position: absolute; + top: 0; + left: -67px; + width: 100%; + height: 20px; + background-color: rgba(255, 255, 255, 0.15); + transform: rotate(125deg); + transform-origin: 50% 50%; + transition: left 0.5s ease-in-out; +} + +.hero-btn::after{ + content: ''; + position: absolute; + top: 0; + left: -34px; + width: 100%; + height: 14px; + background-color: rgba(255, 255, 255, 0.15); + transform: rotate(125deg); + transform-origin: 50% 50%; + transition: left 0.5s ease-out; +} + +.hero-btn:hover::before, +.hero-btn:hover::after { + left: 100%; +} + +.hero-btn:hover { + box-shadow: rgba(0, 0, 0, 0.16) 3px 3px 0px 0px; +} + +@media screen and (max-width: 540px) { + .homepage-hero-section { + background-image: url('/assets/img/homepage-hero-background-large.png'); + aspect-ratio: 1.43; + } + .hero-content { + flex-direction: column; + align-items: center; + justify-content: center; + gap: 2px; + text-align: center; + padding: 8px; + } + .hero-subtitle{ + margin-bottom: 0; + } + + .hero-logo { + width: 80px; + margin: 0; + } + .hero-text-section{ + flex: 0 0 auto; + } + + .hero-title { + display: none; + } + .hero-btn { + font-size: 1rem; + padding: 5px 8px; + margin-top: 4px; + } +} + +/* Home page hero section ends */ +/* Home page key features section starts */ + +@media (min-width: 640px) { + .md-typeset .grid.sm\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +.md-typeset .key-features.grid>.card { + border: none; +} + +.key-features .card { + padding: 0.5rem; + box-shadow: none; + border: none; + background: transparent; + text-align: center; +} +.key-features .card .px-6.py-5 { + padding: 0; + text-align: center; +} + +.key-features .card .key-feature img { + width:132px; + height:132px; + margin:0 auto 10px; +} + +.key-features .card p { + margin: 0; + line-height: 1.2; + font-size: 1em; +} + +.key-features .card p:last-child { + margin-top: 1em; + line-height: 1.75; + font-size: 0.85em; +} + +.key-features .card p b { + font-size: 0.75em; +} + + /* Home page key features section ends */ + +/* Home page popular resources section starts */ +@media (min-width: 640px) { + .md-typeset .grid.sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +.md-typeset .popular-resources .grid>.card { + padding: 0; + box-shadow: none; + border: none; + background: transparent; +} + +.popular-resources .card .px-6.py-5 { + padding: 0; + text-align: center; +} + +.popular-resources .youtube-video a { + display: block; + color: var(--md-typeset-color); + --font-family-headings-custom: "Jersey 10"; + font-family: var(--font-family-headings-custom, + var(--font-inter, sans-serif)); + text-align: left; + margin-top: -6px; +} + +.popular-resources .youtube-video iframe { + border: 2px #000 solid; + border-radius: 0; + padding: 0; + width: 100%; + aspect-ratio: 16 / 9; +} + +.popular-resources .youtube-video a:hover { + text-decoration: underline; +} + +/* Home page popular resources section ends */ + +/* Home page explore content section starts */ + +.explore-content .card { + /* border: 2px #000 solid; */ + border-radius: 0; + padding: 0; + color: var(--md-typeset-color); +} + +.md-typeset .explore-content .grid>.card:hover { + color: var(--md-typeset-color); +} + +.explore-content h2 { + font-size: 0.85rem; + margin-top: 0; +} + +.explore-content .font-normal { + font-size: 0.65rem; + line-height: 1.25rem; + padding-right: 1rem; +} + +/* Home page explore content section ends */ diff --git a/website/mkdocs/docs_src/__init__.py b/website/mkdocs/docs_src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/mkdocs/expand_markdown.py b/website/mkdocs/expand_markdown.py new file mode 100644 index 0000000000..c06c772589 --- /dev/null +++ b/website/mkdocs/expand_markdown.py @@ -0,0 +1,105 @@ +import logging +import re +from pathlib import Path +from typing import Optional + +import typer + +logging.basicConfig(level=logging.INFO) + + +app = typer.Typer() + + +def read_lines_from_file(file_path: Path, lines_spec: Optional[str]) -> str: + """Read lines from a file. + + Args: + file_path: The path to the file. + lines_spec: A comma-separated string of line numbers and/or line ranges. + + Returns: + A string containing the lines from the file. + """ + with file_path.open() as file: + all_lines = file.readlines() + + # Check if lines_spec is empty (indicating all lines should be read) + if not lines_spec: + return "".join(all_lines) + + selected_lines = [] + line_specs = lines_spec.split(",") + + for line_spec in line_specs: + if "-" in line_spec: + # Handle line ranges (e.g., "1-10") + start, end = map(int, line_spec.split("-")) + selected_lines.extend(all_lines[start - 1 : end]) + else: + # Handle single line numbers + line_number = int(line_spec) + if 1 <= line_number <= len(all_lines): + selected_lines.append(all_lines[line_number - 1]) + + return "".join(selected_lines) + + +def extract_lines(embedded_line: str) -> str: + to_expand_path_elements = re.search("{!>(.*)!}", embedded_line).group(1).strip() + lines_spec = "" + if "[ln:" in to_expand_path_elements: + to_expand_path_elements, lines_spec = to_expand_path_elements.split("[ln:") + to_expand_path_elements = to_expand_path_elements.strip() + lines_spec = lines_spec[:-1] + + if Path("./docs/docs_src").exists(): + base_path = Path("./docs") + elif Path("./docs_src").exists(): + base_path = Path("./") + else: + raise ValueError("Couldn't find docs_src directory") + + return read_lines_from_file(base_path / to_expand_path_elements, lines_spec) + + +@app.command() +def expand_markdown( + input_markdown_path: Path = typer.Argument(...), + output_markdown_path: Path = typer.Argument(...), +): + with input_markdown_path.open() as input_file, output_markdown_path.open("w") as output_file: + for line in input_file: + # Check if the line does not contain the "{!>" pattern + if "{!>" not in line: + # Write the line to the output file + output_file.write(line) + else: + output_file.write(extract_lines(embedded_line=line)) + + +def remove_lines_between_dashes(file_path: Path): + with file_path.open() as file: + lines = file.readlines() + + start_dash_index = None + end_dash_index = None + new_lines = [] + + for index, line in enumerate(lines): + if line.strip() == "---": + if start_dash_index is None: + start_dash_index = index + else: + end_dash_index = index + # Remove lines between the two dashes + new_lines = lines[:start_dash_index] + new_lines + lines[end_dash_index + 1 :] + start_dash_index = end_dash_index = None + break # NOTE: Remove this line if you have multiple dash chunks + + with file_path.open("w") as file: + file.writelines(new_lines) + + +if __name__ == "__main__": + app() diff --git a/website/mkdocs/generate_mkdocs.py b/website/mkdocs/generate_mkdocs.py new file mode 100644 index 0000000000..531a09de88 --- /dev/null +++ b/website/mkdocs/generate_mkdocs.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors +# +# SPDX-License-Identifier: Apache-2.0 + +if __name__ == "__main__": + from autogen._website.generate_mkdocs import main + + main() diff --git a/website/mkdocs/includes/.gitkeep b/website/mkdocs/includes/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/mkdocs/mkdocs.yml b/website/mkdocs/mkdocs.yml new file mode 100644 index 0000000000..78c4ee0360 --- /dev/null +++ b/website/mkdocs/mkdocs.yml @@ -0,0 +1,165 @@ +site_name: AG2 +# description to improve website indexing +site_description: A programming framework for agentic AI +site_url: https://ag2ai.github.io/ag2/ +site_author: Chi Wang & Qingyun Wu +copyright: '© 2025 <a href="https://ag2.ai/" target="_blank" rel="noopener">ag2</a>' + +docs_dir: docs + +watch: + - docs + - docs_src + - includes + - overrides + +repo_name: ag2ai/ag2 +repo_url: https://github.com/ag2ai/ag2 +edit_uri: https://github.com/ag2ai/ag2/website/mkdocs/docs + +exclude_docs: | + navigation_template.txt + SUMMARY.md + +theme: + name: material + custom_dir: overrides + logo: assets/img/logo.svg + favicon: assets/img/favicon.ico + font: + text: Inter + code: Roboto Mono + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: custom + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: custom + toggle: + icon: material/brightness-4 + name: Switch to light mode + icon: + repo: fontawesome/brands/github + edit: material/pencil-circle-outline + features: + - search.suggest + - search.highlight + - navigation.tabs # navbar navigation + - navigation.tabs.sticky # navbar always expanded + - navigation.indexes # attach index document direct to section + - navigation.tracking # show current TOC section in the page url + - navigation.prune # reduce render size + - navigation.top # back-to-top btn + - navigation.footer # show footer with next/prev btns + # - navigation.path # (insiders) breadcrumbs + - content.tabs.link # sync total page tabs + # - content.tooltips # (insiders) improved tooltips + - content.code.copy + - content.code.annotate # code annotations with # (1) + # - content.code.select # (insiders) highlight line under cursor + - content.action.edit # add edit btn at every page + +extra_css: + - stylesheets/extra.css + +extra_javascript: + - javascripts/extra.js + +plugins: + - search: + separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' + # - meta # (insiders) use .meta.yml files + - glightbox: # image zoom + manual: true + - macros: # Jinja templates + include_dir: includes + include_yaml: + - people: data/people.yml + - mkdocstrings: # Generate References + default_handler: python + handlers: + python: + inventories: + - https://docs.python.org/3/objects.inv + options: + filters: + - '!^_' + show_root_heading: true + show_if_no_docstring: true + inherited_members: true + members_order: source + separate_signature: true + unwrap_annotated: true + merge_init_into_class: true + docstring_section_style: spacy + signature_crossrefs: true + show_symbol_type_heading: true + show_symbol_type_toc: true + # show_docstring_attributes: false + # show_signature_annotations: true + # load_external_modules: true + - git-revision-date-localized: # show page edition date + enabled: !ENV [CI, false] + type: timeago + - literate-nav: # .md importable navigation + nav_file: SUMMARY.md + - minify: + minify_html: true + minify_js: true + minify_css: true + htmlmin_opts: + remove_comments: true + cache_safe: true + css_files: + - stylesheets/extra.css + - mike: # versioning + alias_type: copy + redirect_template: templates/redirect.html + canonical_version: latest + +hooks: + - create_api_docs.py + +markdown_extensions: + - toc: + permalink: "#" # replace TOC block symbol + toc_depth: 3 + - mdx_include: + base_path: . + line_slice_separator: [] + - extra + - admonition # !!! note blocks support + - pymdownx.details # admonition collapsible + - pymdownx.superfences # highlight code syntax + - pymdownx.highlight: + anchor_linenums: true # allows link to codeline + - pymdownx.inlinehilite # inline code highlighting `#!python <code>` + - pymdownx.tabbed: + alternate_style: true # create tabs group + - attr_list # specify html attrs in markdown + - md_in_html # render md wrapped to html tags + +extra: + analytics: + provider: google + property: G-2GN2KN2CE4 + social_image: https://opengraph.githubassets.com/1671805243.560327/ag2ai/ag2 + social: + # Discord link should be first + - icon: fontawesome/brands/discord + link: https://discord.gg/pAbnFJrkgZ + - icon: fontawesome/brands/github-alt + link: https://github.com/ag2ai/ag2 + - icon: fontawesome/brands/x-twitter + link: https://x.com/ag2oss + - icon: fontawesome/brands/youtube + link: https://www.youtube.com/@ag2ai + - icon: fontawesome/brands/linkedin + link: https://www.linkedin.com/company/ag2ai + + # version: + # provider: mike diff --git a/website/mkdocs/overrides/404.html b/website/mkdocs/overrides/404.html new file mode 100644 index 0000000000..266856be15 --- /dev/null +++ b/website/mkdocs/overrides/404.html @@ -0,0 +1,9 @@ +{% extends "main.html" %} + + <!-- Content --> + {% block content %} + <h1>404 - Page Not Found</h1> + + <p>We could not find what you were looking for.</p> + <p>Please contact the owner of the site that linked you to the original URL and let them know their link is broken.</p> + {% endblock %} diff --git a/website/mkdocs/overrides/home.html b/website/mkdocs/overrides/home.html new file mode 100644 index 0000000000..411aa20772 --- /dev/null +++ b/website/mkdocs/overrides/home.html @@ -0,0 +1,17 @@ +<!-- Elements added to "home.html" will only be displayed on the home page --> + +{% extends "main.html" %} + +{% block styles %} +{{ super() }} +{% endblock %} + +{% block tabs %} +<!-- Main site entry button descriptions --> +{% endblock %} + +{% block content %} +<script> + window.location.href = "docs/home/home"; +</script> +{% endblock %} diff --git a/website/mkdocs/overrides/main.html b/website/mkdocs/overrides/main.html new file mode 100644 index 0000000000..6deba1bd43 --- /dev/null +++ b/website/mkdocs/overrides/main.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block extrahead %} + {% set title = config.site_name %} + {% if page and page.meta and page.meta.title %} + {% set title = title ~ " - " ~ page.meta.title %} + {% elif page and page.title and not page.is_homepage %} + {% set title = title ~ " - " ~ page.title | striptags %} + {% endif %} + <meta property="og:type" content="website" /> + <meta property="og:title" content="{{ title }}" /> + <meta property="og:description" content="{{ config.site_description }}" /> + <meta property="og:url" content="{{ page.canonical_url }}" /> + <meta property="og:image" content="{{ config.extra.social_image }}" /> + <meta property="og:image:type" content="image/png" /> + <meta property="og:image:width" content="1200" /> + <meta property="og:image:height" content="630" /> + + <meta name="twitter:card" content="summary_large_image" /> + <meta name="twitter:title" content="{{ title }}" /> + <meta name="twitter:description" content="{{ config.site_description }}" /> + <meta name="twitter:image" content="{{ config.extra.social_image }}" /> +{% endblock %} + +{% block outdated %} + You're not viewing the latest version. + <a href="{{ '../' ~ base_url }}"> <!-- nosemgrep --> + <strong>Click here to go to latest.</strong> + </a> +{% endblock %} + +{% block announce %} + You're viewing the <strong class="primary-text">work in progress</strong> version of the documentation. + Please click + <a rel="me" href="http://docs.ag2.ai"><strong>here</strong></a> + for the latest. +{% endblock %} diff --git a/website/mkdocs/templates/redirect.html b/website/mkdocs/templates/redirect.html new file mode 100644 index 0000000000..a90c51d87b --- /dev/null +++ b/website/mkdocs/templates/redirect.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>Redirecting + + + + + + + + + + + + + + + + + + + + + + + + Redirecting to {{href}}... + + + diff --git a/website/mkdocs/update_releases.py b/website/mkdocs/update_releases.py new file mode 100644 index 0000000000..72fe561e41 --- /dev/null +++ b/website/mkdocs/update_releases.py @@ -0,0 +1,90 @@ +import re +from pathlib import Path +from typing import List, Sequence, Tuple + +import requests + + +def find_metablock(lines: List[str]) -> Tuple[List[str], List[str]]: + if lines[0] != "---": + return [], lines + + index: int = 0 + for i in range(1, len(lines)): + if lines[i] == "---": + index = i + 1 + + return lines[:index], lines[index:] + + +def find_header(lines: List[str]) -> Tuple[str, List[str]]: + for i in range(len(lines)): + if (line := lines[i]).startswith("#"): + return line, lines[i + 1 :] + + return "", lines + + +def get_github_releases() -> Sequence[Tuple[str, str]]: + # Get the latest version from GitHub releases + response = requests.get("https://api.github.com/repos/airtai/FastStream/releases") + return ((x["tag_name"], x["body"]) for x in reversed(response.json())) + + +def convert_links_and_usernames(text): + if "](" not in text: + # Convert HTTP/HTTPS links + text = re.sub(r"(https?://.*\/(.*))", r'[#\2](\1){.external-link target="_blank"}', text) + + # Convert GitHub usernames to links + text = re.sub( + r"@(\w+) ", + r'[@\1](https://github.com/\1){.external-link target="_blank"} ', + text, + ) + + return text + + +def collect_already_published_versions(text: str) -> List[str]: + data: List[str] = re.findall(r"## (\d.\d.\d.*)", text) + return data + + +def update_release_notes(realease_notes_path: Path): + # Get the changelog from the RELEASE.md file + changelog = realease_notes_path.read_text() + + metablock, lines = find_metablock(changelog.splitlines()) + metablock = "\n".join(metablock) + + header, changelog = find_header(lines) + changelog = "\n".join(changelog) + + old_versions = collect_already_published_versions(changelog) + + for version, body in filter( + lambda v: v[0] not in old_versions, + get_github_releases(), + ): + body = body.replace("##", "###") + body = convert_links_and_usernames(body) + version_changelog = f"## {version}\n\n{body}\n\n" + changelog = version_changelog + changelog + + # Update the RELEASE.md file with the latest version and changelog + realease_notes_path.write_text( + ( + metablock + + "\n\n" + + header + + "\n" # adding an addition newline after the header results in one empty file being added every time we run the script + + changelog + + "\n" + ).replace("\r", "") + ) + + +if __name__ == "__main__": + base_dir = Path(__file__).resolve().parent + update_release_notes(base_dir / "docs" / "en" / "release.md")