Praisonai/issue 1350#1355
Conversation
β¦audit docs - Changed repository reference for install.sh from praisonai-package to MervinPraison/PraisonAI. - Improved sync mechanism to only copy files if there are changes, ensuring the latest version is served. - Added references to official resources and documentation in the security audit workflow. - Included guidelines for creating security advisories and reporting vulnerabilities.
|
Caution Review failedThe pull request is closed. βΉοΈ Recent review infoβοΈ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: β Files ignored due to path filters (2)
π Files selected for processing (35)
π WalkthroughWalkthroughThis PR adds extensive agent workflow documentation, enhances GitHub triage automation with autonomous issue resolution, introduces new CLI port management and Langfuse observability commands, refines tool utilities for git operations and shell command sanitization, and bumps package versions across all Docker images and Python projects to v4.5.139/1.5.139. Changes
Estimated code review effortπ― 4 (Complex) | β±οΈ ~45 minutes Possibly related PRs
Suggested labels
Poem
β¨ Finishing Touchesπ Generate docstrings
π§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR delivers three new features for PraisonAI: (1) a local Langfuse observability platform managed via Key issues found:
Confidence Score: 2/5Not safe to merge as-is β three concrete bugs (dead debug code, CLI crash on Two of the three bugs are P0: the duplicate
Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as praisonai github triage
participant SC as StickyComment
participant Agent as PraisonAI Agent
participant Sink as GithubContextSink
participant GHAPI as GitHub API
CLI->>GHAPI: Fetch issue/PR context
CLI->>SC: Create StickyComment (with YAML todos pre-loaded)
SC->>GHAPI: POST initial comment (spinner + Mermaid flowchart)
CLI->>Agent: Run agent workflow (agents.yaml)
Agent-->>Sink: agent_start / tool_call_start / tool_call_end events
Sink->>SC: on_agent_start / on_tool_start / set_todos
SC-->>GHAPI: PATCH comment (throttled 3s) with progress bar + todo list
Agent->>Agent: Execute tools (git branch, commit, push, create PR)
Sink->>SC: set_branch / set_pr (from tool results)
SC-->>GHAPI: PATCH comment (branch link + PR link)
Agent-->>CLI: Workflow complete
CLI->>SC: finalize(success=True)
SC->>GHAPI: PATCH final comment (β
COMPLETE + summary)
Reviews (1): Last reviewed commit: "feat(workflows): update install.sh sync ..." | Re-trigger Greptile |
| def github_create_branch(branch_name: str) -> str: | ||
| """Create and checkout a new git branch. | ||
|
|
||
| logger.debug(f"Branch '{branch_name}' checked out successfully.") | ||
| Args: | ||
| branch_name: The name of the branch to create and checkout. | ||
| """ |
There was a problem hiding this comment.
logger.debug call placed inside docstring β never executed
The logger.debug(...) line on line 11 is indented at the module level rather than inside the function body, so Python treats it as part of the docstring text. It will never be executed as code, and the docstring will contain the raw string logger.debug(f"Branch '{branch_name}' checked out successfully.") as documentation text.
The call should be moved inside the function body, after the successful git checkout call:
| def github_create_branch(branch_name: str) -> str: | |
| """Create and checkout a new git branch. | |
| logger.debug(f"Branch '{branch_name}' checked out successfully.") | |
| Args: | |
| branch_name: The name of the branch to create and checkout. | |
| """ | |
| def github_create_branch(branch_name: str) -> str: | |
| """Create and checkout a new git branch. | |
| Args: | |
| branch_name: The name of the branch to create and checkout. | |
| """ | |
| try: | |
| # Check if we are in a git repository | |
| subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], check=True, capture_output=True) | |
| subprocess.run(["git", "checkout", "-B", branch_name], check=True, capture_output=True, text=True) | |
| logger.debug(f"Branch '{branch_name}' checked out successfully.") | |
| return f"Successfully created and checked out branch '{branch_name}'" |
| def langfuse_init( | ||
| email: str = typer.Option( | ||
| "admin@langfuse.com", "--email", "-e", help="Admin email address" | ||
| ), | ||
| password: str = typer.Option( | ||
| None, "--password", "-p", help="Admin password (will prompt if not provided)" | ||
| ), | ||
| port: int = typer.Option(3000, "--port", "-p", help="Port to listen on"), | ||
| data_dir: str = typer.Option( | ||
| None, "--data-dir", help="Data directory (default: ~/.praisonai/langfuse)" | ||
| ), |
There was a problem hiding this comment.
Duplicate
-p short flag causes Click/Typer runtime error
Both --password and --port are registered with the short flag -p in langfuse_init. Click will raise a ValueError ("Option '-p' is already registered") at import time or when the command is invoked, making praisonai langfuse init completely non-functional.
Change the short flag for --password to avoid the collision:
| def langfuse_init( | |
| email: str = typer.Option( | |
| "admin@langfuse.com", "--email", "-e", help="Admin email address" | |
| ), | |
| password: str = typer.Option( | |
| None, "--password", "-p", help="Admin password (will prompt if not provided)" | |
| ), | |
| port: int = typer.Option(3000, "--port", "-p", help="Port to listen on"), | |
| data_dir: str = typer.Option( | |
| None, "--data-dir", help="Data directory (default: ~/.praisonai/langfuse)" | |
| ), | |
| password: str = typer.Option( | |
| None, "--password", "-pw", help="Admin password (will prompt if not provided)" | |
| ), | |
| port: int = typer.Option(3000, "--port", "-p", help="Port to listen on"), |
| else: | ||
| console.print(f"[red]β Failed to kill process {proc['pid']}[/red]") | ||
| if not force: | ||
| console.print("[yellow]Try: praisonai port kill {port} --force[/yellow]") |
There was a problem hiding this comment.
Missing
f prefix β {port} renders as a literal string
This string lacks the f prefix, so {port} is never interpolated. The user will see the text Try: praisonai port kill {port} --force instead of the actual port number.
| console.print("[yellow]Try: praisonai port kill {port} --force[/yellow]") | |
| console.print(f"[yellow]Try: praisonai port kill {port} --force[/yellow]") |
There was a problem hiding this comment.
Code Review
This pull request introduces several enhancements, including a new port management CLI command, improved GitHub triage functionality with enhanced sticky comments, and updates to Langfuse integration. My review identified a critical NameError in github_tools.py due to a missing logger definition, an incorrectly placed debug log within a docstring, and a bug in langfuse.py where existing environment variables are overwritten during initialization. Additionally, I provided suggestions to improve the file path regex in github.py and to make the port mapping update in langfuse.py idempotent.
| import subprocess | ||
| from pydantic import BaseModel, Field | ||
| from typing import Dict, Any, Optional | ||
| import logging |
There was a problem hiding this comment.
| def github_create_branch(branch_name: str) -> str: | ||
| """Create and checkout a new git branch. | ||
|
|
||
| logger.debug(f"Branch '{branch_name}' checked out successfully.") |
| env_content = "# Langfuse Initial User Configuration\n" | ||
| env_content += f"LANGFUSE_INIT_USER_EMAIL={default_email}\n" | ||
| env_content += f"LANGFUSE_INIT_USER_PASSWORD={default_password}\n" | ||
| env_content += f"LANGFUSE_INIT_USER_NAME={default_name}\n" | ||
| env_content += f"LANGFUSE_INIT_ORG_NAME=Default Org\n" | ||
| env_content += f"LANGFUSE_INIT_PROJECT_NAME=Default Project\n" | ||
| env_content += "LANGFUSE_INIT_PROJECT_ID=default-project\n" | ||
| env_file.write_text(env_content) |
There was a problem hiding this comment.
This logic overwrites the .env file with only the initial user configuration, causing any other existing environment variables (which were read into env_vars at lines 138-142) to be lost. The code should update the env_vars dictionary and write all its entries back to the file to ensure configuration persistence.
| env_content = "# Langfuse Initial User Configuration\n" | |
| env_content += f"LANGFUSE_INIT_USER_EMAIL={default_email}\n" | |
| env_content += f"LANGFUSE_INIT_USER_PASSWORD={default_password}\n" | |
| env_content += f"LANGFUSE_INIT_USER_NAME={default_name}\n" | |
| env_content += f"LANGFUSE_INIT_ORG_NAME=Default Org\n" | |
| env_content += f"LANGFUSE_INIT_PROJECT_NAME=Default Project\n" | |
| env_content += "LANGFUSE_INIT_PROJECT_ID=default-project\n" | |
| env_file.write_text(env_content) | |
| # Update with Langfuse init user environment variables | |
| env_vars.update({ | |
| "LANGFUSE_INIT_USER_EMAIL": default_email, | |
| "LANGFUSE_INIT_USER_PASSWORD": default_password, | |
| "LANGFUSE_INIT_USER_NAME": default_name, | |
| "LANGFUSE_INIT_ORG_NAME": "Default Org", | |
| "LANGFUSE_INIT_PROJECT_NAME": "Default Project", | |
| "LANGFUSE_INIT_PROJECT_ID": "default-project" | |
| }) | |
| # Write .env file | |
| env_content = "# Langfuse Configuration\n" | |
| env_content += "\n".join([f"{k}={v}" for k, v in env_vars.items()]) | |
| env_file.write_text(env_content) |
| # Check if we are in a git repository | ||
| subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], check=True, capture_output=True) | ||
| subprocess.run(["git", "checkout", "-b", branch_name], check=True, capture_output=True, text=True) | ||
| subprocess.run(["git", "checkout", "-B", branch_name], check=True, capture_output=True, text=True) |
There was a problem hiding this comment.
The debug log should be placed here to correctly report a successful branch checkout, as intended by the pull request's objective.
| subprocess.run(["git", "checkout", "-B", branch_name], check=True, capture_output=True, text=True) | |
| subprocess.run(["git", "checkout", "-B", branch_name], check=True, capture_output=True, text=True) | |
| logger.debug(f"Branch '{branch_name}' checked out successfully.") |
| r"sed\s+-i\s+['\"]?.*?['\"]?\s+['\"]([^'\"]+)['\"]", | ||
| r"cat\s+>\s+['\"]?([^\s'\"]+)['\"]?", | ||
| r"echo\s+.*?\s*>\s*['\"]?([^\s'\"]+)['\"]?", | ||
| r"\.*/([^/]+\.(py|yaml|yml|json|md|txt))", |
There was a problem hiding this comment.
The regex r"\.*/([^/]+\.(py|yaml|yml|json|md|txt))" is too restrictive for extracting file paths. It only matches files in the current directory when prefixed with ./ (e.g., ./config.yaml) and fails to match paths containing subdirectories (e.g., src/main.py) because the [^/]+ pattern explicitly excludes directory separators.
| r"\.*/([^/]+\.(py|yaml|yml|json|md|txt))", | |
| r"(?:^|\s|['\"])([\w./-]+\.(?:py|yaml|yml|json|md|txt))", |
| @@ -119,6 +130,41 @@ def langfuse_start( | |||
| content = content.replace("3000:3000", f"{port}:3000") | |||
There was a problem hiding this comment.
There was a problem hiding this comment.
Pull request overview
This PR bumps PraisonAI / praisonaiagents versions and expands CLI and GitHub-triage functionality (Langfuse trace viewing, port conflict tooling, enhanced sticky comments), along with adding several agent workflow docs/config updates.
Changes:
- Bump wrapper/core versions to
4.5.139/1.5.139and update related lockfiles/Dockerfiles. - Add
praisonai portcommand + tests, and add Langfuse API client +langfuse traces/sessions/showCLI tests. - Enhance GitHub triage sticky comment formatting/tracking; add integration tests and agent workflow docs.
Reviewed changes
Copilot reviewed 34 out of 37 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/praisonai/uv.lock | Updates locked praisonaiagents version. |
| src/praisonai/tests/unit/cli/test_port_command.py | Adds unit tests for new port CLI. |
| src/praisonai/tests/unit/cli/test_langfuse_commands.py | Adds unit tests for Langfuse CLI + client. |
| src/praisonai/pyproject.toml | Bumps praisonaiagents minimum version. |
| src/praisonai/praisonai/version.py | Bumps PraisonAI version to 4.5.139. |
| src/praisonai/praisonai/deploy.py | Updates pinned praisonai version in Dockerfile generator. |
| src/praisonai/praisonai/cli/langfuse_client.py | Introduces Langfuse HTTP API client. |
| src/praisonai/praisonai/cli/commands/port.py | Adds new praisonai port command implementation. |
| src/praisonai/praisonai/cli/commands/langfuse.py | Extends Langfuse CLI (init + traces/sessions/show + better errors). |
| src/praisonai/praisonai/cli/commands/github.py | Enhances sticky PR comment rendering and tracking. |
| src/praisonai/praisonai/cli/app.py | Registers port command with main CLI app. |
| src/praisonai/praisonai.rb | Bumps Homebrew formula tag URL version. |
| src/praisonai-agents/uv.lock | Updates locked praisonaiagents version. |
| src/praisonai-agents/tests/integration/test_github_triage_integration.py | Adds integration tests for triage workflow behavior. |
| src/praisonai-agents/tests/integration/test_github_enhanced_comment.py | Adds integration tests for enhanced sticky comments. |
| src/praisonai-agents/pyproject.toml | Bumps praisonaiagents version to 1.5.139. |
| src/praisonai-agents/praisonaiagents/tools/shell_tools.py | Hardens shell command parsing/edge cases. |
| src/praisonai-agents/praisonaiagents/tools/github_tools.py | Adjusts branch checkout behavior; intended debug logging change. |
| src/praisonai-agents/praisonaiagents/obs/init.py | Loads Langfuse env from ~/.praisonai/langfuse.env in obs.auto(). |
| src/praisonai-agents/praisonaiagents/init.py | Tweaks lazy export handling for obs/db. |
| src/praisonai-agents/.agent/workflows/trigger-claude.md | Adds workflow doc for triggering Claude. |
| src/praisonai-agents/.agent/workflows/security-audit.md | Adds security advisory workflow doc. |
| src/praisonai-agents/.agent/workflows/local-fix.md | Adds local fix workflow doc. |
| src/praisonai-agents/.agent/workflows/instruction.md | Adds agent instruction workflow doc. |
| src/praisonai-agents/.agent/workflows/install-deploy.md | Adds install/deploy workflow doc. |
| src/praisonai-agents/.agent/workflows/create-post.md | Adds content publishing workflow doc. |
| src/praisonai-agents/.agent/workflows/create-github-issue.md | Adds issue creation workflow doc. |
| src/praisonai-agents/.agent/workflows/analysis.md | Adds analysis-only workflow doc. |
| docker/Dockerfile.ui | Bumps minimum praisonai version in UI image. |
| docker/Dockerfile.dev | Bumps minimum praisonai version in dev image. |
| docker/Dockerfile.chat | Bumps minimum praisonai version in chat image. |
| .gitignore | Stops ignoring .agent directory. |
| .github/workflows/auto-pr-comment.yml | Adds Claude fallback trigger job. |
| .github/triage-context.md | Updates triage context to issue #1350. |
| .github/praisonai-issue-triage.yaml | Refactors triage workflow steps/roles and PR-creation conditions. |
| .agent/workflows/instruction.md | Adds root .agent instruction workflow doc. |
| .agent/workflows/analysis.md | Adds root .agent analysis workflow doc. |
π‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def github_create_branch(branch_name: str) -> str: | ||
| """Create and checkout a new git branch. | ||
|
|
||
| logger.debug(f"Branch '{branch_name}' checked out successfully.") | ||
| Args: | ||
| branch_name: The name of the branch to create and checkout. | ||
| """ | ||
| try: | ||
| # Check if we are in a git repository | ||
| subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], check=True, capture_output=True) | ||
| subprocess.run(["git", "checkout", "-b", branch_name], check=True, capture_output=True, text=True) | ||
| subprocess.run(["git", "checkout", "-B", branch_name], check=True, capture_output=True, text=True) | ||
| return f"Successfully created and checked out branch '{branch_name}'" |
| password: str = typer.Option( | ||
| None, "--password", "-p", help="Admin password (will prompt if not provided)" | ||
| ), | ||
| port: int = typer.Option(3000, "--port", "-p", help="Port to listen on"), | ||
| data_dir: str = typer.Option( |
There was a problem hiding this comment.
In langfuse_init, both --password and --port use the same short option -p, which will cause Typer/Click option parsing errors. Use distinct short flags (or drop one short flag).
| # Reuse the start logic with explicit credentials | ||
| ctx = typer.Context(langfuse_start) | ||
| langfuse_start( | ||
| ctx=ctx, | ||
| port=port, | ||
| host="127.0.0.1", | ||
| detach=True, | ||
| data_dir=data_dir, | ||
| email=email, | ||
| password=password | ||
| ) |
There was a problem hiding this comment.
langfuse_init constructs ctx = typer.Context(langfuse_start), but typer.Context/click.Context expects a Click Command, not a Python function. This is likely to raise at runtime. Consider extracting the shared start logic into a helper that doesnβt require a Context, or obtain a real Click command context from the Typer app.
| if len(parts) >= 9: | ||
| return { | ||
| "name": parts[0], | ||
| "pid": parts[1], | ||
| "user": parts[2], | ||
| "type": parts[4], | ||
| "protocol": parts[7] if len(parts) > 7 else "TCP", | ||
| "port": port, | ||
| "raw": data_line |
There was a problem hiding this comment.
For lsof output, parts[7] is the NODE column, not the network protocol. As written, protocol will be incorrect (e.g., 'TCP' info is in the NAME column). Parse the NAME column to determine TCP/UDP (or omit protocol if unavailable).
| if len(parts) >= 9: | ||
| # Extract port from address | ||
| addr = parts[8] if len(parts) > 8 else "" | ||
| port_match = re.search(r":(\d+)", addr) | ||
| if port_match: | ||
| port = int(port_match.group(1)) | ||
| processes.append({ | ||
| "name": parts[0], | ||
| "pid": parts[1], | ||
| "user": parts[2], | ||
| "protocol": parts[7] if len(parts) > 7 else "TCP", | ||
| "port": port, | ||
| "address": addr | ||
| }) |
There was a problem hiding this comment.
Same parsing issue in _get_all_ports: parts[7] corresponds to the NODE column, so protocol is incorrect for lsof entries. Consider deriving protocol from the NAME column or setting a constant when unavailable.
| def _extract_file_from_command(self, command: str) -> None: | ||
| """Extract file paths from shell commands to track modifications.""" | ||
| # Match patterns like: open('file'), sed -i 'file', cat > file, etc. | ||
| patterns = [ | ||
| r"open\(['\"]([^'\"]+)['\"]", | ||
| r"sed\s+-i\s+['\"]?.*?['\"]?\s+['\"]([^'\"]+)['\"]", | ||
| r"cat\s+>\s+['\"]?([^\s'\"]+)['\"]?", | ||
| r"echo\s+.*?\s*>\s*['\"]?([^\s'\"]+)['\"]?", | ||
| r"\.*/([^/]+\.(py|yaml|yml|json|md|txt))", | ||
| ] |
There was a problem hiding this comment.
_extract_file_from_command doesnβt match common patterns used in the tests and real commands (e.g., sed -i '...' src/config.yaml without quoting the filename, or paths like src/config.yaml without a ./ prefix). This will prevent file tracking from working and will likely fail the added integration tests. Extend the regex patterns to capture unquoted paths and paths without a leading ./.
| def test_sticky_comment_has_branch_and_pr_links(self): | ||
| """Verify sticky comment renders branch link and Create PR link correctly.""" | ||
| from praisonai.praisonai.cli.commands.github import StickyComment | ||
|
|
||
| sticky = StickyComment( |
There was a problem hiding this comment.
The import path from praisonai.praisonai.cli.commands.github import StickyComment looks incorrect (double praisonai). This will likely raise ModuleNotFoundError in test runs. Import from the actual wrapper package module (e.g., praisonai.cli.commands.github).
| def test_triage_yaml_structure(self): | ||
| """Verify the triage YAML has correct 2-step structure with conditional PR.""" | ||
| import yaml | ||
|
|
||
| yaml_path = "/Users/praison/praisonai-package/.github/praisonai-issue-triage.yaml" | ||
| assert os.path.exists(yaml_path), f"YAML file not found: {yaml_path}" | ||
|
|
There was a problem hiding this comment.
This test hard-codes an absolute path (/Users/praison/...) which will fail in CI and for other developers. Use a repo-relative path derived from Path(__file__).resolve() (or similar) instead.
| # GitHub Issue #1350: Add debug log on successful branch checkout | ||
|
|
||
| Please thoroughly trace how src/praisonai/praisonai/cli/main.py interfaces with typer across all command modules. Produce a detailed architecture document in a new file docs/cli_architecture_test.md. | ||
| The github_create_branch function in src/praisonai-agents/praisonaiagents/tools/github_tools.py should call logger.debug after a successful checkout. |
There was a problem hiding this comment.
The PR title and triage context point to a narrow change (issue #1350: add a debug log in github_create_branch), but this PR also introduces substantial new CLI features (port management, langfuse trace viewing) and workflow docs. Consider splitting unrelated changes into separate PRs to keep review scope aligned with the stated issue.
| # Add praisonai wrapper to path | ||
| PRAISONAI_ROOT = Path(__file__).parent.parent.parent.parent.parent / "src" / "praisonai" | ||
| if str(PRAISONAI_ROOT) not in sys.path: | ||
| sys.path.insert(0, str(PRAISONAI_ROOT)) |
There was a problem hiding this comment.
PRAISONAI_ROOT = Path(__file__).parent.parent.parent.parent.parent / "src" / "praisonai" likely goes one directory too far (it resolves to the parent of the repo root), so the wrapper package wonβt be importable and these tests will fail in CI. Consider computing the repo root more robustly (e.g., Path(__file__).resolve().parents[4]) or using an env var/test helper to locate the monorepo root.
|
@claude You are the FINAL architecture reviewer. CodeRabbit: 1 comments | Greptile: 1 comments. Review Context: 2 total comments, 2 bot reviews. Phase 1: Review per AGENTS.md principles (protocol-driven, backward compatible, no perf impact). |
|
Claude finished @MervinPraison's task ββ View job β’ Final Architecture Review & Issue Fixes β3-Phase Review Complete: Todo List:
Phase 1: Architecture Review βAssessment: COMPLIANT with AGENTS.md principles β
Protocol-driven: New features properly use core SDK protocols Key architectural strengths:
Phase 2: Critical Bug Fixes βFixed 3 P0 Issues:
Notes on other reviewer issues:
Phase 3: Final Verdict βRECOMMENDATION: APPROVE WITH CONDITIONS MET The PR introduces valuable features:
All critical runtime bugs have been fixed. The features follow PraisonAI architectural principles and maintain backward compatibility. Fixes committed: 9f33858 |
Summary by CodeRabbit
New Features
init,traces,sessions,show) for observability and trace managementBug Fixes
Tests
Chores