Compare commits

...

5 Commits

Author SHA1 Message Date
Greyson LaLonde
214bd2e1e0 fix: always skip current bump in devtools tag release notes
The tag command runs after the bump PR is merged, so the bump commit is
in history regardless of --dry-run. Passing bump_already_done=not dry_run
caused dry-run previews to use the bump commit as the range start and
produce empty release notes.
2026-05-19 23:47:47 +08:00
Greyson LaLonde
435fa2e123 Merge branch 'main' into feat/enterprise-release-notes 2026-05-19 23:38:52 +08:00
Greyson LaLonde
284533464f fix: bump idna to 3.15 to address GHSA-65pc-fj4g-8rjx
Some checks are pending
Build uv cache / build-cache (3.10) (push) Waiting to run
Build uv cache / build-cache (3.11) (push) Waiting to run
Build uv cache / build-cache (3.12) (push) Waiting to run
Build uv cache / build-cache (3.13) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Check Documentation Broken Links / Check broken links (push) Waiting to run
Vulnerability Scan / pip-audit (push) Waiting to run
2026-05-19 23:38:34 +08:00
Greyson LaLonde
a6dc44f145 feat: generate categorized release notes for enterprise 2026-05-19 23:26:05 +08:00
Tiago Freire
024e230b2c docs: remove {" "} JSX expressions breaking <Steps> render (#5857)
## Overview

Prettier-inserted bare `{" "}` lines between sibling `<Step>` elements caused Mintlify's `<Steps>` to crash with "Cannot read properties of undefined (reading 'stepNumber')", leaving the page body blank.

### Affected pages (en/ar/ko/pt-BR):
- enterprise/guides/enable-crew-studio
- learn/llm-selection-guide
2026-05-19 10:44:53 -04:00
10 changed files with 109 additions and 47 deletions

View File

@@ -146,7 +146,6 @@ Crew Studio هو طريقة مبتكرة لإنشاء طواقم وكلاء ال
</Step>
{" "}
<Step title="الإجابة على الأسئلة">
أجب على أسئلة التوضيح من مساعد الطاقم لتنقيح
متطلباتك.
@@ -161,12 +160,10 @@ Crew Studio هو طريقة مبتكرة لإنشاء طواقم وكلاء ال
</Step>
{" "}
<Step title="الموافقة أو التعديل">
وافق على الخطة أو اطلب تغييرات إذا لزم الأمر.
</Step>
{" "}
<Step title="التنزيل أو النشر">
نزّل الكود للتخصيص أو انشر مباشرة على المنصة.
</Step>

View File

@@ -802,7 +802,6 @@ The tables below show a representative sample of current top-performing models a
Begin with well-established models like **GPT-4.1**, **Claude 3.7 Sonnet**, or **Gemini 2.0 Flash** that offer good performance across multiple dimensions and have extensive real-world validation.
</Step>
{" "}
<Step title="Identify Specialized Needs">
Determine if your crew has specific requirements (coding, reasoning, speed)
that would benefit from specialized models like **Claude 4 Sonnet** for
@@ -810,7 +809,6 @@ The tables below show a representative sample of current top-performing models a
consider fast inference providers like **Groq** alongside model selection.
</Step>
{" "}
<Step title="Implement Multi-Model Strategy">
Use different models for different agents based on their roles.
High-capability models for managers and complex tasks, efficient models for

View File

@@ -146,7 +146,6 @@ Here's a typical workflow for creating a crew with Crew Studio:
</Step>
{" "}
<Step title="Answer Questions">
Respond to clarifying questions from the Crew Assistant to refine your
requirements.
@@ -161,12 +160,10 @@ Here's a typical workflow for creating a crew with Crew Studio:
</Step>
{" "}
<Step title="Approve or Modify">
Approve the plan or request changes if necessary.
</Step>
{" "}
<Step title="Download or Deploy">
Download the code for customization or deploy directly to the platform.
</Step>

View File

@@ -805,7 +805,6 @@ The tables below show a representative sample of current top-performing models a
Begin with well-established models like **GPT-4.1**, **Claude 3.7 Sonnet**, or **Gemini 2.0 Flash** that offer good performance across multiple dimensions and have extensive real-world validation.
</Step>
{" "}
<Step title="Identify Specialized Needs">
Determine if your crew has specific requirements (coding, reasoning, speed)
that would benefit from specialized models like **Claude 4 Sonnet** for
@@ -813,7 +812,6 @@ The tables below show a representative sample of current top-performing models a
consider fast inference providers like **Groq** alongside model selection.
</Step>
{" "}
<Step title="Implement Multi-Model Strategy">
Use different models for different agents based on their roles.
High-capability models for managers and complex tasks, efficient models for

View File

@@ -145,7 +145,6 @@ LLM 연결과 기본 설정을 구성했다면 이제 Crew Studio 사용을 시
</Step>
{" "}
<Step title="질문에 답하기">
crew assistant가 요구 사항을 구체화할 수 있도록 하는 추가 질문에 답변하세요.
</Step>
@@ -159,12 +158,10 @@ LLM 연결과 기본 설정을 구성했다면 이제 Crew Studio 사용을 시
</Step>
{" "}
<Step title="승인 또는 수정">
계획을 승인하거나 필요하다면 변경을 요청하세요.
</Step>
{" "}
<Step title="다운로드 또는 배포">
사용자화를 위해 코드를 다운로드하거나 플랫폼에 직접 배포하세요.
</Step>

View File

@@ -797,7 +797,6 @@ LLM 선택을 최적화하고자 하는 팀을 위해 **CrewAI AMP 플랫폼**
여러 차원에서 우수한 성능을 제공하며 실제 환경에서 광범위하게 검증된 **GPT-4.1**, **Claude 3.7 Sonnet**, **Gemini 2.0 Flash**와 같은 잘 알려진 모델부터 시작하십시오.
</Step>
{" "}
<Step title="특화된 요구 사항 식별">
crew에 코드 작성, reasoning, 속도 등 특정 요구가 있는지 확인하고, 이러한
요구에 부합하는 **Claude 4 Sonnet**(개발용) 또는 **o3**(복잡한 분석용)과 같은
@@ -805,7 +804,6 @@ LLM 선택을 최적화하고자 하는 팀을 위해 **CrewAI AMP 플랫폼**
더불어 **Groq**와 같은 빠른 추론 제공자를 고려할 수 있습니다.
</Step>
{" "}
<Step title="다중 모델 전략 구현">
각 에이전트의 역할에 따라 다양한 모델을 사용하세요. 관리자와 복잡한 작업에는
고성능 모델을, 일상적 운영에는 효율적인 모델을 적용합니다.

View File

@@ -146,7 +146,6 @@ Veja um fluxo de trabalho típico para criação de um crew com o Crew Studio:
</Step>
{" "}
<Step title="Responder Perguntas">
Responda às perguntas de esclarecimento do Crew Assistant para refinar seus
requisitos.
@@ -161,12 +160,10 @@ Veja um fluxo de trabalho típico para criação de um crew com o Crew Studio:
</Step>
{" "}
<Step title="Aprovar ou Modificar">
Aprove o plano ou solicite alterações, se necessário.
</Step>
{" "}
<Step title="Baixar ou Fazer Deploy">
Baixe o código para personalização ou faça o deploy diretamente na plataforma.
</Step>

View File

@@ -797,7 +797,6 @@ As tabelas abaixo mostram uma amostra dos modelos de maior destaque em cada cate
Inicie com opções consagradas como **GPT-4.1**, **Claude 3.7 Sonnet** ou **Gemini 2.0 Flash**, que oferecem bom desempenho e ampla validação.
</Step>
{" "}
<Step title="Identifique Demandas Especializadas">
Descubra se sua crew possui requisitos específicos (código, raciocínio,
velocidade) que justifiquem modelos como **Claude 4 Sonnet** para
@@ -805,7 +804,6 @@ As tabelas abaixo mostram uma amostra dos modelos de maior destaque em cada cate
velocidade, considere Groq aliado à seleção do modelo.
</Step>
{" "}
<Step title="Implemente Estratégia Multi-Modelo">
Use modelos diferentes para agentes distintos conforme o papel. Modelos de
alta capacidade para managers e tarefas complexas, eficientes para rotinas.

View File

@@ -744,18 +744,23 @@ def _is_prerelease(version: str) -> bool:
return any(indicator in v for indicator in _PRERELEASE_INDICATORS)
def get_commits_from_last_tag(tag_name: str, version: str) -> tuple[str, str]:
def get_commits_from_last_tag(
tag_name: str, version: str, cwd: Path | None = None
) -> tuple[str, str]:
"""Get commits from the last tag, excluding current version.
Args:
tag_name: Current tag name (e.g., "v1.0.0").
version: Current version (e.g., "1.0.0").
cwd: Directory to run git commands in (defaults to current).
Returns:
Tuple of (commit_range, commits) where commits is newline-separated.
"""
try:
all_tags = run_command(["git", "tag", "--sort=-version:refname"]).split("\n")
all_tags = run_command(
["git", "tag", "--sort=-version:refname"], cwd=cwd
).split("\n")
prev_tags = [t for t in all_tags if t and t != tag_name and t != f"v{version}"]
if not _is_prerelease(version):
@@ -764,22 +769,30 @@ def get_commits_from_last_tag(tag_name: str, version: str) -> tuple[str, str]:
if prev_tags:
last_tag = prev_tags[0]
commit_range = f"{last_tag}..HEAD"
commits = run_command(["git", "log", commit_range, "--pretty=format:%s"])
commits = run_command(
["git", "log", commit_range, "--pretty=format:%s"], cwd=cwd
)
else:
commit_range = "HEAD"
commits = run_command(["git", "log", "--pretty=format:%s"])
commits = run_command(["git", "log", "--pretty=format:%s"], cwd=cwd)
except subprocess.CalledProcessError:
commit_range = "HEAD"
commits = run_command(["git", "log", "--pretty=format:%s"])
commits = run_command(["git", "log", "--pretty=format:%s"], cwd=cwd)
return commit_range, commits
def get_github_contributors(commit_range: str) -> list[str]:
def get_github_contributors(
commit_range: str,
repo: str = "crewAIInc/crewAI",
cwd: Path | None = None,
) -> list[str]:
"""Get GitHub usernames from commit range using GitHub API.
Args:
commit_range: Git commit range (e.g., "abc123..HEAD").
repo: GitHub repo in ``owner/name`` form to resolve commits against.
cwd: Directory to run git commands in (defaults to current).
Returns:
List of GitHub usernames sorted alphabetically.
@@ -791,10 +804,10 @@ def get_github_contributors(commit_range: str) -> list[str]:
gh_token = None
g = Github(login_or_token=gh_token) if gh_token else Github()
github_repo = g.get_repo("crewAIInc/crewAI")
github_repo = g.get_repo(repo)
commit_shas = run_command(
["git", "log", commit_range, "--pretty=format:%H"]
["git", "log", commit_range, "--pretty=format:%H"], cwd=cwd
).split("\n")
contributors = set()
@@ -934,9 +947,26 @@ def _generate_release_notes(
version: str,
tag_name: str,
no_edit: bool,
cwd: Path | None = None,
gh_repo: str = "crewAIInc/crewAI",
openai_client: OpenAI | None = None,
bump_already_done: bool = True,
) -> tuple[str, OpenAI, bool]:
"""Generate, display, and optionally edit release notes.
Args:
version: Version being released.
tag_name: Tag name for the release.
no_edit: Skip the interactive edit prompt.
cwd: Directory to run git commands in (defaults to current).
gh_repo: GitHub repo (``owner/name``) for resolving contributors.
openai_client: Reuse an existing OpenAI client if provided.
bump_already_done: True when the ``feat: bump versions to <version>``
commit for the current release is already in history (the real
release path). False in previews where no bump exists yet — the
most recent bump commit is the *previous* version and must be
used as the range start.
Returns:
Tuple of (release_notes, openai_client, is_prerelease).
"""
@@ -951,7 +981,8 @@ def _generate_release_notes(
"log",
"--grep=^feat: bump versions to",
"--format=%H %s",
]
],
cwd=cwd,
)
bump_entries = [
line for line in prev_bump_output.strip().split("\n") if line.strip()
@@ -959,7 +990,8 @@ def _generate_release_notes(
is_stable = not _is_prerelease(version)
prev_commit = None
for entry in bump_entries[1:]:
scan_entries = bump_entries[1:] if bump_already_done else bump_entries
for entry in scan_entries:
bump_ver = entry.split("feat: bump versions to", 1)[-1].strip()
if is_stable and _is_prerelease(bump_ver):
continue
@@ -969,7 +1001,7 @@ def _generate_release_notes(
if prev_commit:
commit_range = f"{prev_commit}..HEAD"
commits = run_command(
["git", "log", commit_range, "--pretty=format:%s"]
["git", "log", commit_range, "--pretty=format:%s"], cwd=cwd
)
commit_lines = [
@@ -979,14 +1011,21 @@ def _generate_release_notes(
]
commits = "\n".join(commit_lines)
else:
commit_range, commits = get_commits_from_last_tag(tag_name, version)
commit_range, commits = get_commits_from_last_tag(
tag_name, version, cwd=cwd
)
except subprocess.CalledProcessError:
commit_range, commits = get_commits_from_last_tag(tag_name, version)
commit_range, commits = get_commits_from_last_tag(
tag_name, version, cwd=cwd
)
github_contributors = get_github_contributors(commit_range)
github_contributors = get_github_contributors(
commit_range, repo=gh_repo, cwd=cwd
)
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
if openai_client is None:
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
if commits.strip():
contributors_section = ""
@@ -1544,7 +1583,13 @@ def _wait_for_pr_merged(branch: str, cwd: Path) -> None:
time.sleep(_PR_MERGE_POLL_INTERVAL)
def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> None:
def _release_enterprise(
version: str,
is_prerelease: bool,
dry_run: bool,
no_edit: bool = False,
openai_client: OpenAI | None = None,
) -> None:
"""Clone the enterprise repo, bump versions, and create a release PR.
Expects ENTERPRISE_REPO, ENTERPRISE_VERSION_DIRS, and
@@ -1554,6 +1599,8 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
version: New version string.
is_prerelease: Whether this is a pre-release version.
dry_run: Show what would be done without making changes.
no_edit: Skip the interactive release-notes edit prompt.
openai_client: Reuse OpenAI client from earlier phases if available.
"""
if (
not _ENTERPRISE_REPO
@@ -1571,7 +1618,6 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
)
if dry_run:
console.print(f"[dim][DRY RUN][/dim] Would clone {enterprise_repo}")
for d in _ENTERPRISE_VERSION_DIRS:
console.print(f"[dim][DRY RUN][/dim] Would update versions in {d}")
console.print(
@@ -1582,6 +1628,26 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
"[dim][DRY RUN][/dim] Would create bump PR, wait for merge, "
"then tag and release"
)
with tempfile.TemporaryDirectory() as tmp:
repo_dir = Path(tmp) / enterprise_repo.split("/")[-1]
console.print(f"\nCloning {enterprise_repo} (read-only preview)...")
run_command(["gh", "repo", "clone", enterprise_repo, str(repo_dir)])
console.print(f"[green]✓[/green] Cloned {enterprise_repo}")
_generate_release_notes(
version,
version,
no_edit,
cwd=repo_dir,
gh_repo=enterprise_repo,
openai_client=openai_client,
bump_already_done=False,
)
console.print(
"[dim][DRY RUN][/dim] Would tag and create GitHub release "
"with the notes above"
)
return
with tempfile.TemporaryDirectory() as tmp:
@@ -1694,8 +1760,18 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
run_command(["git", "pull"], cwd=repo_dir)
tag_name = version
release_notes, _, _ = _generate_release_notes(
version,
tag_name,
no_edit,
cwd=repo_dir,
gh_repo=enterprise_repo,
openai_client=openai_client,
)
run_command(
["git", "tag", "-a", tag_name, "-m", f"Release {version}"],
["git", "tag", "-a", tag_name, "-m", release_notes],
cwd=repo_dir,
)
run_command(["git", "push", "origin", tag_name], cwd=repo_dir)
@@ -1711,7 +1787,7 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
"--title",
tag_name,
"--notes",
f"Release {version}",
release_notes,
]
if is_prerelease:
gh_cmd.append("--prerelease")
@@ -2010,7 +2086,7 @@ def tag(dry_run: bool, no_edit: bool) -> None:
console.print("[green]✓[/green] main branch up to date")
release_notes, openai_client, is_prerelease = _generate_release_notes(
version, tag_name, no_edit
version, tag_name, no_edit, bump_already_done=True
)
docs_branch = _update_docs_and_create_pr(
@@ -2121,7 +2197,7 @@ def release(
if skip_to_enterprise:
try:
_release_enterprise(version, is_prerelease, dry_run)
_release_enterprise(version, is_prerelease, dry_run, no_edit=no_edit)
except BaseException as e:
_print_release_error(e)
_resume_hint(
@@ -2217,7 +2293,7 @@ def release(
console.print("[green]✓[/green] main branch up to date")
release_notes, openai_client, is_prerelease = _generate_release_notes(
version, tag_name, no_edit
version, tag_name, no_edit, bump_already_done=not dry_run
)
docs_branch = _update_docs_and_create_pr(
@@ -2271,7 +2347,13 @@ def release(
if not skip_enterprise:
try:
_release_enterprise(version, is_prerelease, dry_run)
_release_enterprise(
version,
is_prerelease,
dry_run,
no_edit=no_edit,
openai_client=openai_client,
)
except BaseException as e:
_print_release_error(e)
_resume_hint(

8
uv.lock generated
View File

@@ -13,7 +13,7 @@ resolution-markers = [
]
[options]
exclude-newer = "2026-05-12T13:27:48.906744Z"
exclude-newer = "2026-05-16T15:32:24.373474Z"
exclude-newer-span = "P3D"
[manifest]
@@ -3268,11 +3268,11 @@ wheels = [
[[package]]
name = "idna"
version = "3.11"
version = "3.15"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
{ url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
]
[[package]]