Skip to content

merge#1690

Closed
littlecc9999 wants to merge 0 commit into
MervinPraison:mainfrom
littlecc9999:main
Closed

merge#1690
littlecc9999 wants to merge 0 commit into
MervinPraison:mainfrom
littlecc9999:main

Conversation

@littlecc9999

@littlecc9999 littlecc9999 commented May 19, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Automatic agent generation from natural language prompts
    • Multiple UI interfaces: Web UI, Chat interface, and Code interface
    • Web API server with Docker containerization
    • Support for 100+ LLM models from multiple providers (OpenAI, Groq, Google Gemini, Anthropic, Cohere, Mistral, Ollama)
    • Built-in tool ecosystem including web search, website scraping, and file operations
    • Google Colab notebooks for quick testing
  • Documentation

    • Comprehensive guides for all features and use cases
    • Integration guides for tools and LLM providers
    • Cloud deployment instructions
  • Chores

    • Docker and CI/CD pipeline configuration
    • Project licensing and funding setup

Review Change Stack

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds PraisonAI runtime (CLI, generators, models), Chainlit chat/code UIs with persistence, deployment script, CI workflows, comprehensive docs (MkDocs/Sphinx), examples, tests, Dockerfile, and configs. Updates agents YAMLs and environment samples.

Changes

End-to-end scaffolding and features

Layer / File(s) Summary
Runtime, UIs, CI/CD, docs, and tests
praisonai/*, praisonai/ui/*, .github/workflows/*, docs/**, tests/**, agents*.yaml, Dockerfile, api.py, mkdocs.yml, pyproject.toml, requirements.txt, .env.example, .readthedocs.yml, settings.yaml
Implements CLI-centered engine with Agents/Auto generators, Chainlit chat/code UIs, model resolver, deployment and CI workflows, extensive docs site, sample configs, notebooks, and tests.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

Review effort 4/5, Possible security concern

Suggested reviewers

  • MervinPraison

Poem

In burrows of code, I hop and compile,
Spinning new UIs with a whiskered smile.
Docs sprout like clover, tests gently bloom,
Engines hum softly in the CI room.
Ship it in Docker, to clouds we fly—
A happy hare whispering: Praison AI! 🐰✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch main

@MervinPraison

Copy link
Copy Markdown
Owner

@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above from Qodo, Coderabbit, and Gemini first — incorporate their findings.

Review areas:

  1. Bloat check: Are changes minimal and focused? Any unnecessary code or scope creep?
  2. Security: Any hardcoded secrets, unsafe eval/exec, missing input validation?
  3. Performance: Any module-level heavy imports? Hot-path regressions?
  4. Tests: Are tests included? Do they cover the changes adequately?
  5. Backward compat: Any public API changes without deprecation?
  6. Code quality: DRY violations, naming conventions, error handling?
  7. Address reviewer feedback: If Qodo, Coderabbit, or Gemini flagged valid issues, include them in your review
  8. Suggest specific improvements with code examples where possible

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant updates to the PraisonAI framework, including the addition of new UI components (Chainlit and Gradio), enhanced deployment capabilities, and improved model configuration handling. Several critical security vulnerabilities were identified in the authentication logic across the new UI implementations, where credentials were not being validated correctly. Additionally, there are opportunities to improve configuration management for API keys and model initialization to ensure better error handling and consistency.

Comment thread praisonai/chainlit_ui.py Outdated
def auth_callback(username: str, password: str):
# Fetch the user matching username from your database
# and compare the hashed password with the value stored in the database
if (username, password) == (username, password):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The password authentication callback contains a tautology (username, password) == (username, password), which will always evaluate to True. This effectively disables password protection, allowing any credentials to be accepted. It should compare the input against the expected environment variables.

Suggested change
if (username, password) == (username, password):
if username == os.getenv("CHAINLIT_USERNAME", "admin") and password == os.getenv("CHAINLIT_PASSWORD", "admin"):

Comment thread praisonai/ui/chat.py Outdated

@cl.password_auth_callback
def auth_callback(username: str, password: str):
if (username, password) == (username, password):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The authentication logic here is a tautology and will always grant access regardless of the provided credentials. This is a critical security vulnerability.

Suggested change
if (username, password) == (username, password):
if username == os.getenv("CHAINLIT_USERNAME", "admin") and password == os.getenv("CHAINLIT_PASSWORD", "admin"):

Comment thread praisonai/ui/code.py Outdated
@cl.password_auth_callback
def auth_callback(username: str, password: str):
if (username, password) == (username, password):
return cl.User(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The authentication check is broken as it compares the input parameters to themselves, always returning true. This bypasses security controls.

    if username == os.getenv("CHAINLIT_USERNAME", "admin") and password == os.getenv("CHAINLIT_PASSWORD", "admin"):

Comment thread praisonai/auto.py Outdated
self.client = instructor.patch(
OpenAI(
base_url=self.config_list[0]['base_url'],
api_key=os.getenv("OPENAI_API_KEY"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The api_key is being fetched directly from environment variables, ignoring the self.config_list that was initialized in the constructor. For consistency and to support custom configurations, it should use the value from self.config_list as done for base_url on the previous line.

Suggested change
api_key=os.getenv("OPENAI_API_KEY"),
api_key=self.config_list[0]['api_key'],

Comment thread praisonai/inc/models.py Outdated
return ChatCohere(
model=self.model_name,
cohere_api_key=self.api_key,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Defaulting the API key to the string "nokey" when the environment variable is missing will lead to generic authentication errors from the LLM provider at runtime. It is better to leave it as None or raise a descriptive ValueError immediately to help the user identify the missing configuration.

        self.api_key = os.environ.get(self.api_key_var)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/developers/googlecolab.md (1)

56-229: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wrap the captured run output in a fenced code block.

The transcript is currently indented text and triggers MD046; fencing it will stabilize markdown rendering and linting.

📝 Proposed structure
+```text
      [2024-07-03 04:39:09][DEBUG]: == Working Agent: Space Researcher
      ...
      Continued advancements in detection methods and technologies promise to further expand our knowledge in the years to come.
+```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/developers/googlecolab.md` around lines 56 - 229, The captured run
output (the indented transcript starting with "[2024-07-03 04:39:09][DEBUG]: ==
Working Agent: Space Researcher" through the final line "Continued advancements
in detection methods and technologies promise to further expand our knowledge in
the years to come.") should be wrapped in a fenced code block (``` ... ```) to
prevent MD046 markdown lint errors; edit the section containing that transcript
in the docs/developers/googlecolab.md content and surround the entire transcript
block with triple backticks so the logs are rendered as a single code block.
🟡 Minor comments (32)
.env.example-4-5 (1)

4-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicated CHAINLIT_USERNAME key.

Line 5 duplicates Line 4, which makes the sample ambiguous and error-prone for users copying this file.

Suggested fix
 CHAINLIT_USERNAME=admin
-CHAINLIT_USERNAME=admin
 CHAINLIT_AUTH_SECRET="chainlit create-secret to create"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.example around lines 4 - 5, Remove the duplicate environment variable
key by deleting the redundant CHAINLIT_USERNAME entry so the .env.example
contains only one CHAINLIT_USERNAME definition; ensure the remaining
CHAINLIT_USERNAME line is correct and leave other variables unchanged.
README.md-145-147 (1)

145-147: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make visible URLs consistent with actual links.

The displayed domain (docs.praisonai.com) differs from the href target (docs.praison.ai), which is confusing for readers.

🔗 Suggested fix
-| **UI**    | Multi Agents such as CrewAI or AutoGen     | [https://docs.praisonai.com/ui/ui](https://docs.praison.ai/ui/ui)     |
-| **Chat**  | Chat with 100+ LLMs, single AI Agent       | [https://docs.praisonai.com/ui/chat](https://docs.praison.ai/ui/chat) |
-| **Code**  | Chat with entire Codebase, single AI Agent | [https://docs.praisonai.com/ui/code](https://docs.praison.ai/ui/code) |
+| **UI**    | Multi Agents such as CrewAI or AutoGen     | [https://docs.praison.ai/ui/ui](https://docs.praison.ai/ui/ui)     |
+| **Chat**  | Chat with 100+ LLMs, single AI Agent       | [https://docs.praison.ai/ui/chat](https://docs.praison.ai/ui/chat) |
+| **Code**  | Chat with entire Codebase, single AI Agent | [https://docs.praison.ai/ui/code](https://docs.praison.ai/ui/code) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 145 - 147, The README table shows visible domains as
"docs.praisonai.com" but links point to "docs.praison.ai"; update the visible
link text to match the actual hrefs (or change the hrefs to match the displayed
domains) for the **UI**, **Chat**, and **Code** rows so the displayed URLs and
the target URLs are consistent (e.g., ensure the table entries for "**UI**",
"**Chat**", and "**Code**" use the same domain string as their hrefs).
README.md-159-161 (1)

159-161: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language to the fenced code block.

This currently violates markdown lint (MD040) and hurts syntax rendering.

📝 Suggested fix
-```
+```bash
 python -m praisonai ui
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @README.md around lines 159 - 161, The fenced code block containing the
command "python -m praisonai ui" lacks a language tag and triggers MD040; update
the block in README.md to include a language identifier (e.g., bash) so it
becomes bash ... to fix markdown linting and improve syntax rendering for
the "python -m praisonai ui" snippet.


</details>

</blockquote></details>
<details>
<summary>README.md-73-83 (1)</summary><blockquote>

`73-83`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix broken Table of Contents fragments.**

At least two anchors don’t match section headings, so those TOC links won’t navigate correctly.



<details>
<summary>🧭 Suggested fix</summary>

```diff
-- [Installation](`#installation`)
+- [Installation Multi Agents](`#installation-multi-agents`)
...
-- [Other Models](`#other-models`)
+- [Use 100+ Models](`#use-100-models`)
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 73 - 83, Fix the broken TOC anchors in README.md by
matching each link target to the exact heading text (including spelling,
capitalization, and punctuation); specifically verify and correct entries like
[Initialise](`#initialise`) and [Praison AI Chat](`#praison-ai-chat`) (and any
others listed) so their fragment identifiers exactly match the corresponding
section headings, updating the link text or the anchor slug to the precise
heading string used in the document.
```

</details>

</blockquote></details>
<details>
<summary>cookbooks/praisonai-googlecolab.ipynb-230-232 (1)</summary><blockquote>

`230-232`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fail fast when `OPENAI_API_KEY` is missing.**

Line 231 silently falls back to a placeholder value, which leads to confusing runtime authentication failures. Validate and raise a clear error instead.




<details>
<summary>💡 Proposed fix (cell source)</summary>

```diff
- os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY') or "ENTER OPENAI_API_KEY HERE"
+ api_key = userdata.get("OPENAI_API_KEY")
+ if not api_key:
+     raise ValueError("Set OPENAI_API_KEY in Colab Secrets before running this notebook.")
+ os.environ["OPENAI_API_KEY"] = api_key
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbooks/praisonai-googlecolab.ipynb` around lines 230 - 232, Replace the
silent fallback for OPENAI_API_KEY: instead of assigning
os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY') or "ENTER
OPENAI_API_KEY HERE", check the value returned from
userdata.get('OPENAI_API_KEY') (or the relevant input source) and if it's
missing/empty raise a clear exception (or call sys.exit with an explanatory
message) so the notebook fails fast with a descriptive error about the missing
OPENAI_API_KEY rather than using a placeholder.
```

</details>

</blockquote></details>
<details>
<summary>cookbooks/praisonai-tools-googlecolab.ipynb-217-219 (1)</summary><blockquote>

`217-219`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Guard against missing `OPENAI_API_KEY` instead of using a placeholder.**

Line 218 currently sets a fake key when secrets are absent; this defers failure to a less actionable API error path.




<details>
<summary>💡 Proposed fix (cell source)</summary>

```diff
- os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY') or "ENTER OPENAI_API_KEY HERE"
+ api_key = userdata.get("OPENAI_API_KEY")
+ if not api_key:
+     raise ValueError("Set OPENAI_API_KEY in Colab Secrets before running this notebook.")
+ os.environ["OPENAI_API_KEY"] = api_key
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbooks/praisonai-tools-googlecolab.ipynb` around lines 217 - 219, The code
currently assigns a placeholder key into os.environ["OPENAI_API_KEY"] using
userdata.get('OPENAI_API_KEY') or "ENTER OPENAI_API_KEY HERE", which hides
missing-secret errors; change this so you only set os.environ["OPENAI_API_KEY"]
when userdata.get('OPENAI_API_KEY') returns a non-empty value and otherwise
raise a clear RuntimeError (or display an explicit message) instructing the user
to add the OPENAI_API_KEY secret in Colab, referencing the os.environ assignment
and the userdata.get('OPENAI_API_KEY') call to locate and update the logic.
```

</details>

</blockquote></details>
<details>
<summary>docs/developers/googlecolab-tools.md-71-207 (1)</summary><blockquote>

`71-207`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fence the execution transcript as a code block.**

The output section is indented text instead of a fenced block, which triggers MD046 and can render inconsistently.



<details>
<summary>📝 Proposed structure</summary>

```diff
+```text
      [2024-07-03 04:53:48][DEBUG]: == Working Agent: Space Researcher
      ...
      The continuous exploration and study of exoplanets will undoubtedly lead to new insights and perhaps even the discovery of life beyond our solar system.
+```
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/developers/googlecolab-tools.md around lines 71 - 207, The execution
transcript under the "Final Answer" / "Task Output" sections is currently
rendered as indented text (triggering MD046); replace that indented block with a
fenced code block (triple backticks) so the entire transcript (lines starting at
the timestamps like "[2024-07-03 04:53:48]..." through the final paragraph) is
enclosed between and, preserving content exactly; locate the transcript
by searching for the "Final Answer:" and "### Task Output ###" markers and wrap
the intervening text in a fenced block.


</details>

</blockquote></details>
<details>
<summary>docs/developers/wrapper.md-37-37 (1)</summary><blockquote>

`37-37`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix wording typo in setup note.**

“before hand” should be “beforehand” in this user-facing instruction.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/developers/wrapper.md` at line 37, Change the user-facing sentence in
docs/developers/wrapper.md from “Note: Please create agents.yaml file before
hand.” to use the correct single-word form “beforehand” (e.g., “Note: Please
create agents.yaml file beforehand.”) so the setup instruction reads correctly.
```

</details>

</blockquote></details>
<details>
<summary>docs/home.md-84-84 (1)</summary><blockquote>

`84-84`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix typo in section heading.**

Line 84 has a spelling error: `initialse` → `initialise` (or `initialize`, if you prefer US spelling).

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/home.md` at line 84, Update the heading text "To initialse with a
specific agent framework (Optional):" to correct the spelling error by replacing
"initialse" with either "initialise" (British) or "initialize" (US) so the
heading reads "To initialise with a specific agent framework (Optional):" (or
"To initialize with a specific agent framework (Optional):") in docs/home.md.
```

</details>

</blockquote></details>
<details>
<summary>docs/models/openai.md-32-32 (1)</summary><blockquote>

`32-32`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Avoid duplicate section headings in the same document.**

Rename the second `### agents.yaml file` heading (for example, `### agents.yaml file (explicit model in role)`) to resolve MD024.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/openai.md` at line 32, The document contains a duplicate heading
"### agents.yaml file" which triggers MD024; locate the second occurrence of the
"### agents.yaml file" heading in docs/models/openai.md and rename it to a
unique title such as "### agents.yaml file (explicit model in role)" (or another
descriptive variant) so the two sections are distinct while preserving the
original content and context.
```

</details>

</blockquote></details>
<details>
<summary>docs/models/groq.md-7-7 (1)</summary><blockquote>

`7-7`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix heading level increment in this section title.**

Change `### agents.yaml file` to `## agents.yaml file` to satisfy MD001.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/groq.md` at line 7, Update the Markdown heading "### agents.yaml
file" to use one less hash so it becomes "## agents.yaml file" to correct the
heading level (MD001); locate the heading string "### agents.yaml file" in the
docs and replace it with "## agents.yaml file".
```

</details>

</blockquote></details>
<details>
<summary>docs/models/openai.md-55-55 (1)</summary><blockquote>

`55-55`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Hyphenate “Multi-Agent” in this heading for consistency.**

Consider changing `## Using Multi Agents` to `## Using Multi-Agent Setup` (or `## Using Multi-Agents`) for clearer terminology.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/openai.md` at line 55, Update the heading "## Using Multi Agents"
to hyphenated and clearer wording; replace the text "## Using Multi Agents" with
"## Using Multi-Agent Setup" (or "## Using Multi-Agents") in the docs file so
the section title uses "Multi-Agent" consistently across documentation.
```

</details>

</blockquote></details>
<details>
<summary>docs/models/ollama.md-3-3 (1)</summary><blockquote>

`3-3`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Adjust heading depth to fix markdownlint MD001.**

`### agents.yaml file` should be `## agents.yaml file` so heading levels are sequential.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/ollama.md` at line 3, Change the heading "### agents.yaml file"
to be one level higher ("## agents.yaml file") so the document's heading
hierarchy is sequential and resolves markdownlint MD001; locate the heading text
"agents.yaml file" in docs/models/ollama.md and replace the leading "###" with
"##".
```

</details>

</blockquote></details>
<details>
<summary>docs/models/google.md-10-10 (1)</summary><blockquote>

`10-10`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Correct heading hierarchy for markdownlint compliance.**

Update `### agents.yaml file` to `## agents.yaml file` to fix the heading-increment rule violation.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/google.md` at line 10, Change the markdown heading "###
agents.yaml file" to "## agents.yaml file" in the docs/models/google.md content
to correct the heading hierarchy; locate the line containing the exact string
"### agents.yaml file" and update it to "## agents.yaml file" so it complies
with the markdownlint heading-increment rule.
```

</details>

</blockquote></details>
<details>
<summary>docs/models/anthropic.md-10-10 (1)</summary><blockquote>

`10-10`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix heading level jump to satisfy markdown linting.**

Change `### agents.yaml file` to `## agents.yaml file` to avoid the MD001 heading-increment violation.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/anthropic.md` at line 10, Change the markdown heading "###
agents.yaml file" to a level 2 heading "## agents.yaml file" in
docs/models/anthropic.md to fix the MD001 heading-increment lint violation;
update the single heading text so it matches the surrounding document structure.
```

</details>

</blockquote></details>
<details>
<summary>docs/models/cohere.md-10-10 (1)</summary><blockquote>

`10-10`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Use an H2 heading here to prevent MD001 warning.**

`### agents.yaml file` should be `## agents.yaml file` so heading levels increment by one.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/cohere.md` at line 10, Replace the H3 heading "### agents.yaml
file" with an H2 heading "## agents.yaml file" in the docs/models/cohere.md
content so heading levels increment properly and avoid the MD001 warning; locate
the line containing the exact string "### agents.yaml file" and change it to "##
agents.yaml file".
```

</details>

</blockquote></details>
<details>
<summary>docs/models/openrouter.md-7-7 (1)</summary><blockquote>

`7-7`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix heading level jump to satisfy markdown lint.**

Line 7 uses `###` directly after a top-level `#` heading. Use `##` here to avoid MD001 lint failures.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/openrouter.md` at line 7, The heading "agents.yaml file"
currently uses a level-3 header (###) immediately after the top-level title,
causing an MD001 lint failure; change that heading to a level-2 header (##
agents.yaml file) so it follows the top-level `#` and resolves the markdown lint
error.
```

</details>

</blockquote></details>
<details>
<summary>docs/models/openrouter.md-26-27 (1)</summary><blockquote>

`26-27`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Remove empty tool entry from the YAML example.**

Line 27 sets a tool value to `''`, which creates an invalid/misleading example when users copy it. Replace it with a real tool name or remove `tools` entirely from this snippet.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/models/openrouter.md` around lines 26 - 27, The YAML example contains an
invalid empty tool entry under the tools key (the line `- ''`); remove the empty
entry or replace it with a real tool identifier (e.g., a concrete tool name) so
the snippet is valid and not misleading — update the `tools:` block in the
example to either omit `tools` entirely if unused or include a proper tool name
instead of `''`.
```

</details>

</blockquote></details>
<details>
<summary>docs/requirements.txt-7-14 (1)</summary><blockquote>

`7-14`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Remove the duplicate `mkdocs-jupyter` entry.**

`mkdocs-jupyter` is listed twice in the file. Keep a single entry to avoid resolver ambiguity.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/requirements.txt` around lines 7 - 14, Remove the duplicate package
entry for mkdocs-jupyter in the requirements list: locate the two occurrences of
"mkdocs-jupyter" and keep only a single line with that package name (remove the
second duplicate) so the requirements file contains unique entries and avoids
resolver ambiguity.
```

</details>

</blockquote></details>
<details>
<summary>docs/tools/crawl4ai.md-25-25 (1)</summary><blockquote>

`25-25`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Remove stray character in schema description.**

Line 25 contains `ßfor`; this should be `for`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/crawl4ai.md` at line 25, The schema description for the Field
named output_fee contains a stray character ("ß") in the string; update the
Field definition for output_fee (the output_fee: str = Field(...,
description="Fee for output token ßfor the OpenAI model.")) to remove the stray
character so the description reads "Fee for output token for the OpenAI model.".
```

</details>

</blockquote></details>
<details>
<summary>docs/tldr.md-3-3 (1)</summary><blockquote>

`3-3`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix section heading typo ("throught" → "through").**

Line 3 has a spelling error in a top-level instruction heading; please correct it for readability.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tldr.md` at line 3, Fix the typo in the top-level heading "## Run
throught Terminal" by changing it to "## Run through Terminal" so the section
reads correctly; update the heading string in the docs (search for the exact
heading "## Run throught Terminal") to the corrected text.
```

</details>

</blockquote></details>
<details>
<summary>docs/tools.md-3-3 (1)</summary><blockquote>

`3-3`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Use standard wording: "Inbuilt/Built-in Tools" instead of "Inbuild Tools".**

Line 3 uses nonstandard wording in a section header; please correct it.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools.md` at line 3, Replace the nonstandard header "## Inbuild Tools"
with the standard wording; locate the header string "## Inbuild Tools" in
docs/tools.md and change it to either "## Inbuilt Tools" or "## Built-in Tools"
(prefer "Built-in Tools" for consistency).
```

</details>

</blockquote></details>
<details>
<summary>docs/tools/custom.md-88-88 (1)</summary><blockquote>

`88-88`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix likely broken relative link to `tools.py`.**

Line 88 links to `tools.py` in the same directory, but that file is not present in the provided docs tree. Point this to an existing path or replace with inline snippet reference.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/custom.md` at line 88, The markdown link text "tools.py" points to
a non-existent relative file; update the link target so it references the
correct existing file path in the repo or replace the link with an inline code
snippet of the tools.py content; locate the broken link by searching for the
"Create a file called tools.py" line / the "[tools.py](tools.py)" link in the
docs and either change the href to the correct path or paste the example code
inline and remove the broken reference.
```

</details>

</blockquote></details>
<details>
<summary>docs/tools/mem0.md-1-3 (1)</summary><blockquote>

`1-3`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix product name typo and opening sentence grammar.**

Line 1 should use `PraisonAI` (not `PaisonAI`), and Line 3 should be rewritten for clarity (e.g., “Mem0 is a tool to store, update, delete, and retrieve memories.”).

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/mem0.md` around lines 1 - 3, Fix the product name typo and correct
the opening sentence: change "PaisonAI" to "PraisonAI" in the header/title and
rewrite the third line to read "Mem0 is a tool to store, update, delete, and
retrieve memories." so the product name and grammar are both corrected (update
the markdown lines in docs/tools/mem0.md).
```

</details>

</blockquote></details>
<details>
<summary>docs/ui/code.md-143-145 (1)</summary><blockquote>

`143-145`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Add a language identifier to the fenced code block.**

The fence at Line 143 is missing a language, which is triggering markdownlint MD040.
 

<details>
<summary>💡 Suggested doc patch</summary>

```diff
-* ```
+* ```bash
   PRAISONAI_MAX_TOKENS=1000000
   ```
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/ui/code.md around lines 143 - 145, The fenced code block containing the
environment line "PRAISONAI_MAX_TOKENS=1000000" is missing a language identifier
and triggers markdownlint MD040; update the triple-backtick fence from tobash (or another appropriate language like sh) so the block becomes bash ... to satisfy the linter and improve syntax highlighting.


</details>

</blockquote></details>
<details>
<summary>docs/tools/you.com.md-8-10 (1)</summary><blockquote>

`8-10`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Add missing `BaseTool` import to the code example.**

The snippet uses `BaseTool` without importing it. Without `from praisonai_tools import BaseTool`, running this code raises `NameError: BaseTool is not defined`.

<details>
<summary>Suggested doc patch</summary>

```diff
 ```python
+from praisonai_tools import BaseTool
 from langchain_community.utilities.you import YouSearchAPIWrapper
 class YouSearchTool(BaseTool):
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/you.com.md` around lines 8 - 10, The example uses BaseTool but
doesn't import it, causing NameError; add the missing import for BaseTool (from
praisonai_tools) at the top of the snippet so the class definition
YouSearchTool(BaseTool) can resolve, keeping the existing import for
YouSearchAPIWrapper intact.
```

</details>

</blockquote></details>
<details>
<summary>docs/tools/wikipedia.md-9-10 (1)</summary><blockquote>

`9-10`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Add missing `BaseTool` import in the Python example.**

`BaseTool` is referenced but not imported, so the snippet fails at class definition time.

<details>
<summary>💡 Suggested doc patch</summary>

```diff
 # tools.py
+from praisonai_tools import BaseTool
 from langchain_community.utilities import WikipediaAPIWrapper
 class WikipediaSearchTool(BaseTool):
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/wikipedia.md` around lines 9 - 10, The example uses BaseTool but
doesn't import it; update the imports at the top of the snippet to include
BaseTool (alongside WikipediaAPIWrapper) so the class definition for
WikipediaSearchTool can resolve BaseTool. Locate the import statement
referencing WikipediaAPIWrapper and add BaseTool to that import list (or add a
separate import for BaseTool) to fix the NameError when defining class
WikipediaSearchTool.
```

</details>

</blockquote></details>
<details>
<summary>docs/tools/tavily.md-3-15 (1)</summary><blockquote>

`3-15`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Add required `TAVILY_API_KEY` environment variable setup before the code example.**

This snippet will fail at runtime without the API key exported. The documentation should follow the established pattern used in similar tools (e.g., `reddit.md`, `you.com.md`) by showing the environment variable setup first.

<details>
<summary>Suggested fix</summary>

```diff
 # Tavily PraisonAI Integration
 
+```bash
+export TAVILY_API_KEY=xxxxxxxx
+```
+
 ```python
 from praisonai_tools import BaseTool
 from langchain.utilities.tavily_search import TavilySearchAPIWrapper
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/tavily.md` around lines 3 - 15, Add an environment-variable setup
line before the example so the TavilySearchAPIWrapper can authenticate: include
a bash export for TAVILY_API_KEY (e.g., export TAVILY_API_KEY=xxxxxxxx)
immediately above the Python code block in the TavilyTool example; keep the
existing class and call to TavilySearchAPIWrapper (TavilyTool and
TavilySearchAPIWrapper) unchanged, just prepend the documented export line
following the same pattern used in reddit.md/you.com.md.
```

</details>

</blockquote></details>
<details>
<summary>docs/ui/ui.md-34-36 (1)</summary><blockquote>

`34-36`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix duplicated env var in credentials example.**

Line 35 repeats `CHAINLIT_USERNAME`; it should be `CHAINLIT_PASSWORD`, otherwise the documented login override won’t work.

<details>
<summary>Suggested fix</summary>

```diff
 CHAINLIT_USERNAME=admin
-CHAINLIT_USERNAME=admin
+CHAINLIT_PASSWORD=admin
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ui/ui.md` around lines 34 - 36, The credentials example repeats the
environment variable CHAINLIT_USERNAME; replace the second occurrence with
CHAINLIT_PASSWORD so the documented login override works correctly (e.g., keep
CHAINLIT_USERNAME=admin and change the duplicate line to
CHAINLIT_PASSWORD=<password_or_value>).
```

</details>

</blockquote></details>
<details>
<summary>mkdocs.yml-118-119 (1)</summary><blockquote>

`118-119`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Move `custom_dir` under `theme` so overrides are applied.**

`custom_dir` must be placed under the `theme` section, not `extra`. Placing it under `extra` means MkDocs will not recognize it as a theme customization directive, and your custom template overrides will silently fail to load.

<details>
<summary>Suggested fix</summary>

```diff
 theme:
   name: material
+  custom_dir: docs/overrides
   features:
@@
-extra:
-  custom_dir: docs/overrides
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mkdocs.yml` around lines 118 - 119, Move the custom_dir key from the extra
section into the theme section in mkdocs.yml so MkDocs recognizes theme
overrides; edit mkdocs.yml to remove custom_dir: docs/overrides from under extra
and add it under the existing theme: block (preserving the path and YAML
indentation), e.g., ensure theme: contains custom_dir: docs/overrides alongside
any theme name or palette entries.
```

</details>

</blockquote></details>
<details>
<summary>praisonai/deploy.py-91-95 (1)</summary><blockquote>

`91-95`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Default API key placeholder may leak into deployed service.**

If `OPENAI_API_KEY` is not set, the default `"Enter your API key"` will be deployed to Cloud Run via `--set-env-vars`, causing silent failures at runtime rather than a clear deployment error.


<details>
<summary>🛡️ Proposed fix: fail early if key is missing</summary>

```diff
     def set_environment_variables(self):
         """Sets environment variables with fallback to .env values or defaults."""
         os.environ["OPENAI_MODEL_NAME"] = os.getenv("OPENAI_MODEL_NAME", "gpt-4o")
-        os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "Enter your API key")
+        api_key = os.getenv("OPENAI_API_KEY")
+        if not api_key:
+            raise ValueError("OPENAI_API_KEY environment variable is required for deployment")
+        os.environ["OPENAI_API_KEY"] = api_key
         os.environ["OPENAI_API_BASE"] = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/deploy.py` around lines 91 - 95, The current
set_environment_variables method sets OPENAI_API_KEY to a placeholder "Enter
your API key", which can leak into deployments; change set_environment_variables
(the method named set_environment_variables) to require a real OPENAI_API_KEY by
reading os.getenv("OPENAI_API_KEY") and if it's missing or equals the
placeholder, raise a clear exception (or call sys.exit with an error) so
deployment fails early; keep setting OPENAI_MODEL_NAME and OPENAI_API_BASE with
their fallbacks but do not supply a default for OPENAI_API_KEY—validate and
abort with a descriptive error referencing "OPENAI_API_KEY".
```

</details>

</blockquote></details>
<details>
<summary>pyproject.toml-6-6 (1)</summary><blockquote>

`6-6`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Specify a valid SPDX license identifier.**

The empty `license` field can cause compliance and security scanning tools to flag this package as having unknown licensing. Use a standard SPDX identifier such as `license = "MIT"` or `license = "Apache-2.0"`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` at line 6, The pyproject.toml currently has an empty license
field; replace license = "" with a valid SPDX identifier (for example license =
"MIT" or license = "Apache-2.0") in pyproject.toml so compliance and security
scanners can detect the project license; optionally add or verify a
corresponding LICENSE file if required by your chosen SPDX identifier.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (13)</summary><blockquote>

<details>
<summary>cookbooks/praisonai-tools-googlecolab.ipynb (1)</summary><blockquote>

`46-189`: _⚡ Quick win_

**Remove committed execution outputs from the notebook.**

These saved outputs add substantial noise to PRs and quickly become stale in docs/examples.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbooks/praisonai-tools-googlecolab.ipynb` around lines 46 - 189, The
notebook contains committed cell outputs (the "outputs" arrays with
"stdout"/"text" content) that should be removed; open
cookbooks/praisonai-tools-googlecolab.ipynb, remove all non-empty "outputs"
arrays from each cell (clear the "outputs" entries) and set any
"execution_count" values to null, then save the notebook (or run a tool like
nbstripout/nbconvert --clear-output) so the file contains no saved execution
outputs.
```

</details>

</blockquote></details>
<details>
<summary>cookbooks/praisonai-googlecolab.ipynb (1)</summary><blockquote>

`24-204`: _⚡ Quick win_

**Clear notebook outputs before committing.**

The captured stdout block is very large and stale, which increases diff noise and makes cookbook maintenance harder.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbooks/praisonai-googlecolab.ipynb` around lines 24 - 204, The notebook
contains a large stale "outputs" stdout block that should be removed before
committing; clear all output cells by removing the "outputs" array entries
(specifically the "stdout" stream blocks) and any large captured text in
notebook cells, and ensure cell output metadata is cleared; apply this change to
the notebook JSON (remove "outputs": [...] and clear "execution_count") or run a
standard cleanup (e.g., Jupyter's Clear All Outputs, nbconvert --clear-output,
or nbstripout) and add a pre-commit hook so future commits auto-strip outputs.
```

</details>

</blockquote></details>
<details>
<summary>docs/index.rst (1)</summary><blockquote>

`9-14`: _⚡ Quick win_

**Populate the root toctree to avoid empty navigation/warnings.**

The `toctree` is currently empty, so Sphinx navigation is blank and can emit build warnings.

<details>
<summary>♻️ Proposed fix</summary>

```diff
 .. toctree::
    :maxdepth: 2
    :caption: Contents:
+ 
+   installation
+   initialise
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.rst` around lines 9 - 14, The root toctree directive is empty (the
.. toctree:: block with caption "Contents"), causing blank navigation and build
warnings; populate that toctree by adding the top-level documentation page names
(e.g., introduction, getting-started, usage, api) as entries under the ..
toctree:: directive or mark it :hidden: if you intentionally want no visible
entries; update the .. toctree:: block (caption: Contents) to include the
appropriate rst filenames (without extensions) so Sphinx builds a proper
navigation tree.
```

</details>

</blockquote></details>
<details>
<summary>docs/requirements.txt (1)</summary><blockquote>

`1-24`: _🏗️ Heavy lift_

**Pin versions in `docs/requirements.txt` for reproducible builds.**

All 24 packages in this file are unpinned. Using unspecified versions can cause documentation output to drift across different build environments and CI runs. Pin each dependency to an exact version (e.g., `sphinx==7.2.6`) or generate from a `requirements.in` file with a pinning step.

</blockquote></details>
<details>
<summary>docs/ui/code.md (1)</summary><blockquote>

`43-43`: _⚡ Quick win_

**Strengthen security guidance around default `admin/admin` credentials.**

Please explicitly instruct users to change default credentials immediately on first login to avoid insecure deployments.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ui/code.md` at line 43, Update the docs sentence that reads "Username
and Password will be asked for the first time. `admin` is the default username
and password." to explicitly instruct users to change the default admin
credentials immediately on first login (e.g., "Change the default admin/admin
password immediately via the account or settings page"), recommend choosing a
strong unique password, and mention disabling or rotating the default admin
account where possible; ensure the text references the default credential string
"admin/admin" so readers know which account to change.
```

</details>

</blockquote></details>
<details>
<summary>praisonai/deploy.py (2)</summary><blockquote>

`128-134`: _💤 Low value_

**Remove unnecessary f-prefix and consider clearer error message.**

Line 133 has an f-string without placeholders. Also, silently continuing after deployment failures may leave the system in a partially deployed state.


<details>
<summary>♻️ Proposed fix</summary>

```diff
             except subprocess.CalledProcessError as e:
                 print(f"ERROR: Command '{e.cmd}' failed with exit status {e.returncode}")
-                print(f"Continuing with the next command...")
+                print("Continuing with the next command...")
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/deploy.py` around lines 128 - 134, In the for loop that iterates
over commands and calls subprocess.run (the block catching
subprocess.CalledProcessError as e), remove the unnecessary f-prefix from the
print that has no placeholders, replace the simple prints with a clearer error
message that includes e.cmd and e.returncode, and do not silently
continue—either re-raise the exception or call sys.exit(1) after logging so
deployment stops on failure rather than proceeding to subsequent commands.
```

</details>

---

`55-61`: _💤 Low value_

**Hardcoded package version will become stale.**

The Dockerfile pins `praisonai==0.0.57`, which will quickly become outdated. Consider making this configurable or using the current package version dynamically.


<details>
<summary>♻️ Suggested approach</summary>

```diff
+from praisonai.version import __version__
+
 class CloudDeployer:
     ...
     def create_dockerfile(self):
         ...
-            file.write("RUN pip install flask praisonai==0.0.57 gunicorn markdown\n")
+            file.write(f"RUN pip install flask praisonai=={__version__} gunicorn markdown\n")
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/deploy.py` around lines 55 - 61, The Dockerfile generation in
deploy.py hardcodes praisonai==0.0.57 which will become stale; update the
Dockerfile write logic (the with open("Dockerfile", "w") block that writes RUN
pip install ...) to inject a configurable or dynamically-determined version
instead of the literal "praisonai==0.0.57" (e.g., read a version variable from
environment, a constant in deploy.py, or derive from package metadata/version
file) and write that variable into the RUN pip install line so the
CMD/COPY/EXPOSE lines remain unchanged.
```

</details>

</blockquote></details>
<details>
<summary>praisonai/inbuilt_tools/autogen_tools.py (1)</summary><blockquote>

`11-16`: _💤 Low value_

**Duplicate import of `os`.**

`os` is imported twice (lines 11 and 14).


<details>
<summary>♻️ Remove duplicate import</summary>

```diff
 import os
 import importlib
 from pathlib import Path
-import os
 import inspect
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/inbuilt_tools/autogen_tools.py` around lines 11 - 16, Remove the
duplicate import of the os module: in the top-level imports of autogen_tools.py
you have "import os" twice (the entries shown in the diff); delete the redundant
second "import os" so each module is imported only once and leave the other
imports (importlib, Path, inspect, sys) intact.
```

</details>

</blockquote></details>
<details>
<summary>tests/search-tool-agents.yaml (1)</summary><blockquote>

`27-28`: _⚡ Quick win_

**Use empty tool lists instead of empty-string tool names.**

`- ''` creates a tool entry with an empty identifier. Use `[]` when a role has no tools.


<details>
<summary>Suggested patch</summary>

```diff
   screenwriter:
@@
-    tools:
-    - ''
+    tools: []
@@
   director:
@@
-    tools:
-    - ''
+    tools: []
```
</details>


Also applies to: 40-41

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/search-tool-agents.yaml` around lines 27 - 28, Replace the incorrect
empty-string tool entries under the tools key (instances where tools: is
followed by - '') with an actual empty list literal (tools: []) so roles show no
tools rather than a tool with an empty identifier; update each occurrence of the
tools mapping that currently uses "- ''" (the YAML key "tools" in the roles
entries) to use [] instead.
```

</details>

</blockquote></details>
<details>
<summary>tests/inbuilt-tool-agents.yaml (1)</summary><blockquote>

`27-28`: _⚡ Quick win_

**Use empty tool lists instead of empty-string tool names.**

`- ''` is a concrete string value, not “no tools”. Prefer `[]` to avoid invalid/ambiguous tool resolution.


<details>
<summary>Suggested patch</summary>

```diff
   screenwriter:
@@
-    tools:
-    - ''
+    tools: []
@@
   director:
@@
-    tools:
-    - ''
+    tools: []
```
</details>


Also applies to: 40-41

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/inbuilt-tool-agents.yaml` around lines 27 - 28, Replace the explicit
empty-string list entry under the "tools" key (the "- ''" item) with an actual
empty list to represent no tools; locate the "tools" mapping in
tests/inbuilt-tool-agents.yaml and change the sequence from containing the
empty-string item to an empty sequence (tools: []) so the parser doesn't treat
an empty string as a concrete tool name; apply the same replacement for the
other occurrence noted around the second tools block.
```

</details>

</blockquote></details>
<details>
<summary>tests/yaml_example.py (1)</summary><blockquote>

`32-39`: _⚡ Quick win_

**Avoid executing the example at import time.**

`praisonai.run()` and `print(result)` should be behind a main guard to keep this module import-safe.

 

<details>
<summary>Suggested fix</summary>

```diff
-# Create a PraisonAI instance with the agent_yaml content
-praisonai = PraisonAI(agent_yaml=agent_yaml)
-
-# Run PraisonAI
-result = praisonai.run()
-
-# Print the result
-print(result)
+if __name__ == "__main__":
+    praisonai = PraisonAI(agent_yaml=agent_yaml)
+    result = praisonai.run()
+    print(result)
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/yaml_example.py` around lines 32 - 39, The example currently executes
at import by instantiating PraisonAI with agent_yaml and calling praisonai.run()
followed by print(result); wrap the instantiation and execution (the
PraisonAI(...) call, praisonai.run(), and print(result)) inside a main guard (if
__name__ == "__main__":) so the example only runs when the script is executed
directly and remains import-safe.
```

</details>

</blockquote></details>
<details>
<summary>tests/source/crewai_tools.py (1)</summary><blockquote>

`64-71`: _⚡ Quick win_

**Guard runtime execution to avoid import side effects.**

`crew.kickoff()` and `print(result)` run on import. This is brittle for test discovery and module reuse.

 

<details>
<summary>Suggested fix</summary>

```diff
-# Create and kick off the crew
-crew = Crew(
-    agents=[researcher, writer],
-    tasks=[research_task, writing_task]
-)
-
-result = crew.kickoff(inputs={'topic': 'renewable energy'})
-print(result)
+def main():
+    crew = Crew(
+        agents=[researcher, writer],
+        tasks=[research_task, writing_task]
+    )
+    result = crew.kickoff(inputs={'topic': 'renewable energy'})
+    print(result)
+
+if __name__ == "__main__":
+    main()
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/source/crewai_tools.py` around lines 64 - 71, The file currently
creates a Crew instance and calls crew.kickoff() as well as print(result) at
import time; move this runtime execution behind a module guard so importing the
module doesn't run tests. Wrap the creation of Crew (using the existing
researcher, writer, research_task, writing_task), the kickoff call
(crew.kickoff(inputs={'topic': 'renewable energy'})) and the print(result) so
they only execute under if __name__ == "__main__":, leaving class/variable
definitions (Crew, researcher, writer, research_task, writing_task) at module
level untouched.
```

</details>

</blockquote></details>
<details>
<summary>tests/tools/internet_search.py (1)</summary><blockquote>

`5-9`: _⚡ Quick win_

**Harden external call handling for search tool.**

External search failures currently bubble up and can fail unrelated tests; also return type is less deterministic without explicit `list(...)`.

  

<details>
<summary>Suggested fix</summary>

```diff
 `@tool`("Internet Search Tool")
 def internet_search_tool(query: str) -> list:
     """Search Internet for relevant information based on a query."""
-    ddgs = DDGS()
-    results = ddgs.text(keywords=query, region='wt-wt', safesearch='moderate', max_results=5)
-    return results
+    try:
+        with DDGS() as ddgs:
+            return list(ddgs.text(keywords=query, region='wt-wt', safesearch='moderate', max_results=5))
+    except Exception:
+        return []
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/tools/internet_search.py` around lines 5 - 9, The internet_search_tool
function currently lets DDGS() failures propagate and returns a generator-like
object; update internet_search_tool to catch exceptions around the DDGS().text
call (catch broad Exception), convert the returned results to an explicit list
(e.g., list(results)) before returning, and on error return an empty list (or a
deterministic fallback) while optionally logging the exception; reference the
DDGS instantiation and the results variable in the try/except surrounding the
call to DDGS().text.
```

</details>

</blockquote></details>

</blockquote></details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `1e5e85b2-32df-4583-8635-9ab8df93e99b`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 8a4e8a3a432b3c3657f3ed357b75e97ae78b9174 and 31ab50201cb690c5651ac936f44661d4b7969893.

</details>

<details>
<summary>⛔ Files ignored due to path filters (48)</summary>

* `docs/images/architecture-dark.png` is excluded by `!**/*.png`
* `docs/images/architecture-light.png` is excluded by `!**/*.png`
* `docs/images/praisonai-agentops-monitoring.png` is excluded by `!**/*.png`
* `docs/images/praisonai-android-chrome-192x192.png` is excluded by `!**/*.png`
* `docs/images/praisonai-android-chrome-512x512.png` is excluded by `!**/*.png`
* `docs/images/praisonai-apple-touch-icon.png` is excluded by `!**/*.png`
* `docs/images/praisonai-logo-black-large.png` is excluded by `!**/*.png`
* `docs/images/praisonai-logo-black.png` is excluded by `!**/*.png`
* `docs/images/praisonai-logo-large.png` is excluded by `!**/*.png`
* `docs/images/praisonai-logo.png` is excluded by `!**/*.png`
* `docs/images/ui-step-1.png` is excluded by `!**/*.png`
* `docs/images/ui-step-10.png` is excluded by `!**/*.png`
* `docs/images/ui-step-11.png` is excluded by `!**/*.png`
* `docs/images/ui-step-2.png` is excluded by `!**/*.png`
* `docs/images/ui-step-3.png` is excluded by `!**/*.png`
* `docs/images/ui-step-4.png` is excluded by `!**/*.png`
* `docs/images/ui-step-5.png` is excluded by `!**/*.png`
* `docs/images/ui-step-6.png` is excluded by `!**/*.png`
* `docs/images/ui-step-7.png` is excluded by `!**/*.png`
* `docs/images/ui-step-8.png` is excluded by `!**/*.png`
* `docs/images/ui-step-9.png` is excluded by `!**/*.png`
* `docs/overrides/images/praisonai-logo-dark.png` is excluded by `!**/*.png`
* `docs/overrides/images/praisonai-logo-light.png` is excluded by `!**/*.png`
* `poetry.lock` is excluded by `!**/*.lock`
* `praisonai/public/android-chrome-192x192.png` is excluded by `!**/*.png`
* `praisonai/public/android-chrome-512x512.png` is excluded by `!**/*.png`
* `praisonai/public/apple-touch-icon.png` is excluded by `!**/*.png`
* `praisonai/public/fantasy.svg` is excluded by `!**/*.svg`
* `praisonai/public/favicon-16x16.png` is excluded by `!**/*.png`
* `praisonai/public/favicon-32x32.png` is excluded by `!**/*.png`
* `praisonai/public/favicon.ico` is excluded by `!**/*.ico`
* `praisonai/public/game.svg` is excluded by `!**/*.svg`
* `praisonai/public/logo_dark.png` is excluded by `!**/*.png`
* `praisonai/public/logo_light.png` is excluded by `!**/*.png`
* `praisonai/public/movie.svg` is excluded by `!**/*.svg`
* `praisonai/public/thriller.svg` is excluded by `!**/*.svg`
* `praisonai/ui/public/fantasy.svg` is excluded by `!**/*.svg`
* `praisonai/ui/public/game.svg` is excluded by `!**/*.svg`
* `praisonai/ui/public/logo_dark.png` is excluded by `!**/*.png`
* `praisonai/ui/public/logo_light.png` is excluded by `!**/*.png`
* `praisonai/ui/public/movie.svg` is excluded by `!**/*.svg`
* `praisonai/ui/public/thriller.svg` is excluded by `!**/*.svg`
* `public/fantasy.svg` is excluded by `!**/*.svg`
* `public/game.svg` is excluded by `!**/*.svg`
* `public/logo_dark.png` is excluded by `!**/*.png`
* `public/logo_light.png` is excluded by `!**/*.png`
* `public/movie.svg` is excluded by `!**/*.svg`
* `public/thriller.svg` is excluded by `!**/*.svg`

</details>

<details>
<summary>📒 Files selected for processing (141)</summary>

* `.env.example`
* `.github/FUNDING.yml`
* `.github/workflows/docker-publish.yml`
* `.github/workflows/python-package.yml`
* `.github/workflows/python-publish.yml`
* `.github/workflows/unittest.yml`
* `.gitignore`
* `.praisonignore`
* `.praisoninclude`
* `.readthedocs.yml`
* `Dockerfile`
* `LICENSE`
* `README.md`
* `agents-advanced.yaml`
* `agents.yaml`
* `api.py`
* `chainlit.md`
* `cookbooks/agents_playbook/agents-scrapewebitetool.yaml`
* `cookbooks/agents_playbook/create-movie-agents.yaml`
* `cookbooks/agents_playbook/examples/researchers.yml`
* `cookbooks/agents_playbook/examples/scriptwriters.yml`
* `cookbooks/agents_playbook/finance-research.yaml`
* `cookbooks/agents_playbook/internet-research-agents.yaml`
* `cookbooks/agents_playbook/medical-research-pubmed-agents.yaml`
* `cookbooks/praisonai-googlecolab.ipynb`
* `cookbooks/praisonai-tools-googlecolab.ipynb`
* `cookbooks/tools/tools.py`
* `docs/CNAME`
* `docs/Makefile`
* `docs/api.md`
* `docs/api/praisonai/agents_generator.html`
* `docs/api/praisonai/auto.html`
* `docs/api/praisonai/cli.html`
* `docs/api/praisonai/deploy.html`
* `docs/api/praisonai/inbuilt_tools/autogen_tools.html`
* `docs/api/praisonai/inbuilt_tools/index.html`
* `docs/api/praisonai/inc/index.html`
* `docs/api/praisonai/inc/models.html`
* `docs/api/praisonai/index.html`
* `docs/api/praisonai/test.html`
* `docs/api/praisonai/ui/context.html`
* `docs/api/praisonai/ui/index.html`
* `docs/api/praisonai/version.html`
* `docs/auto.md`
* `docs/conf.py`
* `docs/contributing.md`
* `docs/deploy.md`
* `docs/developers/agents-playbook.md`
* `docs/developers/googlecolab-tools.md`
* `docs/developers/googlecolab.md`
* `docs/developers/test.md`
* `docs/developers/wrapper-tools.md`
* `docs/developers/wrapper.md`
* `docs/firecrawl.md`
* `docs/framework/autogen.md`
* `docs/framework/crewai.md`
* `docs/generate_docs.sh`
* `docs/generate_from_readme.sh`
* `docs/home.md`
* `docs/images/architecture-dark.excalidraw`
* `docs/images/architecture.excalidraw`
* `docs/index.md`
* `docs/index.rst`
* `docs/initialise.md`
* `docs/installation.md`
* `docs/make.bat`
* `docs/models.md`
* `docs/models/anthropic.md`
* `docs/models/cohere.md`
* `docs/models/google.md`
* `docs/models/groq.md`
* `docs/models/mistral.md`
* `docs/models/ollama.md`
* `docs/models/openai.md`
* `docs/models/openrouter.md`
* `docs/models/other.md`
* `docs/monitoring/agentops.md`
* `docs/overrides/css/custom.css`
* `docs/overrides/js/custom.js`
* `docs/requirements.in`
* `docs/requirements.txt`
* `docs/run.md`
* `docs/tldr.md`
* `docs/tools.md`
* `docs/tools/crawl4ai.md`
* `docs/tools/custom.md`
* `docs/tools/duckduckgo.md`
* `docs/tools/gpt.md`
* `docs/tools/langchain.md`
* `docs/tools/mem0.md`
* `docs/tools/reddit.md`
* `docs/tools/tavily.md`
* `docs/tools/wikipedia.md`
* `docs/tools/you.com.md`
* `docs/tools/youtube.md`
* `docs/tools_test/__init__.py`
* `docs/tools_test/internet_search.py`
* `docs/ui/chat.md`
* `docs/ui/code.md`
* `docs/ui/ui.md`
* `mkdocs.yml`
* `praisonai/__init__.py`
* `praisonai/__main__.py`
* `praisonai/agents_generator.py`
* `praisonai/auto.py`
* `praisonai/chainlit_ui.py`
* `praisonai/cli.py`
* `praisonai/deploy.py`
* `praisonai/inbuilt_tools/__init__.py`
* `praisonai/inbuilt_tools/autogen_tools.py`
* `praisonai/inc/__init__.py`
* `praisonai/inc/models.py`
* `praisonai/test.py`
* `praisonai/ui/chat.py`
* `praisonai/ui/code.py`
* `praisonai/ui/context.py`
* `praisonai/ui/sql_alchemy.py`
* `pyproject.toml`
* `requirements.txt`
* `settings.yaml`
* `tests/__init__.py`
* `tests/advanced_example.py`
* `tests/agents-advanced.yaml`
* `tests/agents.yaml`
* `tests/auto_example.py`
* `tests/autogen-agents.yaml`
* `tests/basic_example.py`
* `tests/crewai-agents.yaml`
* `tests/debug.py`
* `tests/inbuilt-tool-agents.yaml`
* `tests/search-tool-agents.yaml`
* `tests/source/autogen_function_tools.py`
* `tests/source/autogen_langchain_tools.py`
* `tests/source/crewai_tools.py`
* `tests/source/radius.txt`
* `tests/test.py`
* `tests/test_agents_playbook.py`
* `tests/tools/__init__.py`
* `tests/tools/internet_search.py`
* `tests/yaml_example.py`
* `tools.py`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread .github/workflows/unittest.yml Outdated
Comment on lines +20 to +21
curl -sSL https://install.python-poetry.org | python3 -
export PATH="$HOME/.local/bin:$PATH"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Persist Poetry binary path across steps.

export PATH=... on Line 21 only affects that step; poetry install on Line 25 can fail with command-not-found.

Suggested fix
     - name: Install Poetry
       run: |
         curl -sSL https://install.python-poetry.org | python3 -
-        export PATH="$HOME/.local/bin:$PATH"
+        echo "$HOME/.local/bin" >> "$GITHUB_PATH"

Also applies to: 25-26

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/unittest.yml around lines 20 - 21, The workflow exports
Poetry into PATH only in the current step (the export PATH line), so subsequent
steps (like the step that runs poetry install) can't find the poetry binary;
update the job to persist the PATH by appending the Poetry bin dir to the
GITHUB_PATH file (i.e., replace or augment the current export PATH usage with a
command that writes "$HOME/.local/bin" into GITHUB_PATH) immediately after the
install curl step so the poetry binary is available to later steps (including
the step that runs poetry install).

Comment thread api.py Outdated
Comment on lines +14 to +15
html_output = markdown.markdown(output)
return f'<html><body>{html_output}</body></html>'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

find . -name "api.py" -type f

Repository: MervinPraison/PraisonAI

Length of output: 76


🏁 Script executed:

cat -n api.py | head -30

Repository: MervinPraison/PraisonAI

Length of output: 581


🏁 Script executed:

# Check if praisonai.run() output could contain user input or untrusted data
rg -n "def run" --max-count=5 || echo "PraisonAI is external dependency"

# Check if there are any other HTML sanitization measures in place
rg -i "sanitize|escape|bleach|xss" api.py

Repository: MervinPraison/PraisonAI

Length of output: 513


🏁 Script executed:

cat -n praisonai/ui/context.py | sed -n '220,250p'

Repository: MervinPraison/PraisonAI

Length of output: 1303


🏁 Script executed:

rg -n "class PraisonAI" --type py

Repository: MervinPraison/PraisonAI

Length of output: 153


🏁 Script executed:

sed -n '31,150p' praisonai/cli.py | head -100

Repository: MervinPraison/PraisonAI

Length of output: 4336


🏁 Script executed:

rg -n "def generate_crew_and_kickoff" praisonai/cli.py -A 30 | head -50

Repository: MervinPraison/PraisonAI

Length of output: 1682


🏁 Script executed:

rg -n "class AgentsGenerator" --type py

Repository: MervinPraison/PraisonAI

Length of output: 123


🏁 Script executed:

rg -n "def generate_crew_and_kickoff" praisonai/agents_generator.py -A 20

Repository: MervinPraison/PraisonAI

Length of output: 1521


🏁 Script executed:

python3 << 'EOF'
import markdown

# Test 1: Default behavior with HTML
test_html = "<script>alert('xss')</script>"
result = markdown.markdown(test_html)
print("Test 1 - Raw HTML/script in markdown:")
print(f"Input: {test_html}")
print(f"Output: {result}")
print()

# Test 2: HTML in markdown text
test_html2 = "This is text with <img src=x onerror=alert('xss')> tag"
result2 = markdown.markdown(test_html2)
print("Test 2 - HTML event handler in markdown:")
print(f"Input: {test_html2}")
print(f"Output: {result2}")
print()

# Test 3: Check if there are extensions that allow raw HTML
test_with_extensions = markdown.markdown(test_html, extensions=['extra'])
print("Test 3 - With 'extra' extension:")
print(f"Output: {test_with_extensions}")
EOF

Repository: MervinPraison/PraisonAI

Length of output: 426


🏁 Script executed:

# Check for bleach in dependencies
rg "bleach" setup.py requirements.txt pyproject.toml 2>/dev/null || echo "Bleach not found in dependencies"

# Test bleach effectiveness
python3 << 'EOF'
try:
    import bleach
    print("bleach is installed")
    
    # Test bleach.clean() with the same dangerous inputs
    test_script = "<script>alert('xss')</script>"
    test_img = "This is text with <img src=x onerror=alert('xss')> tag"
    
    result1 = bleach.clean(test_script, strip=True)
    result2 = bleach.clean(test_img, strip=True)
    
    print(f"bleach.clean('{test_script}', strip=True) = '{result1}'")
    print(f"bleach.clean('{test_img}', strip=True) = '{result2}'")
except ImportError:
    print("bleach is not installed")
EOF

Repository: MervinPraison/PraisonAI

Length of output: 124


Sanitize rendered Markdown output before embedding into HTML to prevent XSS attacks.

The markdown.markdown() function passes through raw HTML and script tags unchanged. When PraisonAI.run() output (which may contain untrusted content) is directly embedded in the HTML response, it creates an XSS vulnerability.

🛡️ Suggested fix
 from flask import Flask
 from praisonai import PraisonAI
 import markdown
+import bleach
 ...
 def home():
     output = basic()
-    html_output = markdown.markdown(output)
-    return f'<html><body>{html_output}</body></html>'
+    html_output = markdown.markdown(output)
+    safe_html = bleach.clean(html_output, strip=True)
+    return f'<html><body>{safe_html}</body></html>'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
html_output = markdown.markdown(output)
return f'<html><body>{html_output}</body></html>'
html_output = markdown.markdown(output)
safe_html = bleach.clean(html_output, strip=True)
return f'<html><body>{safe_html}</body></html>'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api.py` around lines 14 - 15, The HTML response embeds untrusted rendered
Markdown via markdown.markdown(output) into html_output and returns it directly,
creating an XSS risk; update the code that calls markdown.markdown (and the
return that uses html_output) to sanitize the rendered HTML before embedding—use
a trusted HTML sanitizer (e.g., bleach.clean) to strip/escape script and
dangerous tags and attributes or apply a strict allowlist of tags/attributes, or
configure the Markdown renderer to disallow raw HTML, then return the sanitized
html_output string instead of the raw output from markdown.markdown.

Comment thread api.py Outdated
Comment on lines +17 to +18
if __name__ == "__main__":
app.run(debug=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

fd -t f "api.py" | head -20

Repository: MervinPraison/PraisonAI

Length of output: 74


🏁 Script executed:

cat -n api.py | head -25

Repository: MervinPraison/PraisonAI

Length of output: 581


Do not run Flask with debug=True in committed code. Debug mode exposes sensitive internals and enables the interactive debugger, creating a significant security risk in any deployment beyond local development.

Suggested fix
 if __name__ == "__main__":
-    app.run(debug=True)
+    app.run(debug=False)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if __name__ == "__main__":
app.run(debug=True)
if __name__ == "__main__":
app.run(debug=False)
🧰 Tools
🪛 ast-grep (0.42.2)

[warning] 17-17: Detected Flask app with debug=True. Do not deploy to production with this flag enabled as it will leak sensitive information. Instead, consider using Flask configuration variables or setting 'debug' using system environment variables.
Context: app.run(debug=True)
Note: [CWE-489] Active Debug Code. [REFERENCES]
- https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/

(debug-enabled-python)

🪛 Ruff (0.15.13)

[error] 18-18: Use of debug=True in Flask app detected

(S201)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api.py` around lines 17 - 18, Remove the hardcoded debug=True in the app.run
call when __name__ == "__main__" to avoid enabling Flask's interactive debugger
in committed code; update the startup to run without debug enabled (e.g., call
app.run() or derive the flag from a secure environment/config setting) and
ensure any use of app.config['DEBUG'] or FLASK_DEBUG is driven by
environment-driven config so production never starts with debug=True; locate the
app.run(...) call and the __main__ block to make this change.

Comment thread cookbooks/tools/tools.py Outdated
Comment on lines +59 to +65
def __init__(self):
super().__init__()
interpreter.auto_run = True

def _run(self, code: str):
result = interpreter.chat(code)
return result

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Avoid unconditional code execution in OpenInterpreterTool.

This implementation globally enables auto_run and executes arbitrary prompt content immediately, removing safety confirmation and increasing RCE risk in agent flows.

🔒 Suggested hardening
 class OpenInterpreterTool(BaseTool):
@@
-    def __init__(self):
+    def __init__(self, auto_run: bool = False):
         super().__init__()
-        interpreter.auto_run = True
+        self.auto_run = auto_run
 
     def _run(self, code: str):
-        result = interpreter.chat(code)
-        return result
+        previous = interpreter.auto_run
+        try:
+            interpreter.auto_run = self.auto_run
+            return interpreter.chat(code)
+        finally:
+            interpreter.auto_run = previous
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbooks/tools/tools.py` around lines 59 - 65, The class currently enables
interpreter.auto_run unconditionally in __init__ and immediately executes input
via _run calling interpreter.chat, which risks RCE; change __init__ to avoid
forcing interpreter.auto_run=True (accept a constructor parameter like
auto_run=False or omit modification) and update _run to perform a safe execution
flow: validate or sanitize the code, require an explicit allow_auto_run flag or
confirmation before invoking interpreter.chat/run, and consider using a
non-executing preview API or a sandboxed interpreter method instead of direct
interpreter.chat; refer to the __init__, _run, interpreter.auto_run, and
interpreter.chat symbols when making these changes.

Comment thread docs/api/praisonai/cli.html Outdated
Comment on lines +212 to +213
if unknown_args and unknown_args[0] == &#39;-b&#39; and unknown_args[1] == &#39;api:app&#39;:
args.agent_file = &#39;agents.yaml&#39;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Guard unknown_args length before indexing position 1.

This branch can raise IndexError when exactly one unknown argument is present.

✅ Safe guard
-if unknown_args and unknown_args[0] == '-b' and unknown_args[1] == 'api:app':
+if len(unknown_args) >= 2 and unknown_args[0] == '-b' and unknown_args[1] == 'api:app':
     args.agent_file = 'agents.yaml'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if unknown_args and unknown_args[0] == &#39;-b&#39; and unknown_args[1] == &#39;api:app&#39;:
args.agent_file = &#39;agents.yaml&#39;
if len(unknown_args) >= 2 and unknown_args[0] == '-b' and unknown_args[1] == 'api:app':
args.agent_file = 'agents.yaml'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/api/praisonai/cli.html` around lines 212 - 213, The branch accessing
unknown_args[0] and unknown_args[1] can raise IndexError when unknown_args has
fewer than 2 elements; update the conditional around the unknown_args check in
the code that sets args.agent_file so it first guards the length (e.g., ensure
len(unknown_args) >= 2) before comparing unknown_args[0] == '-b' and
unknown_args[1] == 'api:app', and only then assign args.agent_file =
'agents.yaml'.

Comment on lines +55 to +66
if os.path.isfile(tools_py_path):
logging.info(f"{tools_py_path} exists in the root directory. Loading {tools_py_path} and skipping tools folder.")
tools_module = importlib.import_module("tools")
elif tools_dir_path.is_dir():
logging.info(f"tools folder exists in the root directory. Loading {tool_name} from tools/{tool_name}.py.")
tools_module = importlib.import_module(f"tools.{tool_name}")

# Create autogen_TOOL_NAME_HERE function for each tool
if tools_module is not None:
for name, obj in inspect.getmembers(tools_module):
if inspect.isclass(obj):
globals()[f"autogen_{name}"] = create_autogen_tool_function(name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

tool_name is undefined at module scope, causing NameError.

When the tools directory exists (line 58), the code attempts to use tool_name (lines 59-60) which is not defined at module level. This will crash on import.

🐛 Proposed fix: iterate over tool files in the directory
 if os.path.isfile(tools_py_path):
     logging.info(f"{tools_py_path} exists in the root directory. Loading {tools_py_path} and skipping tools folder.")
     tools_module = importlib.import_module("tools")
-elif tools_dir_path.is_dir():
-    logging.info(f"tools folder exists in the root directory. Loading {tool_name} from tools/{tool_name}.py.")
-    tools_module = importlib.import_module(f"tools.{tool_name}")
+    # Create autogen_TOOL_NAME_HERE function for each tool
+    if tools_module is not None:
+        for name, obj in inspect.getmembers(tools_module):
+            if inspect.isclass(obj):
+                globals()[f"autogen_{name}"] = create_autogen_tool_function(name)
+elif tools_dir_path.is_dir():
+    # Load each .py file in the tools directory
+    for tool_file in tools_dir_path.glob("*.py"):
+        if tool_file.name.startswith("_"):
+            continue
+        tool_name = tool_file.stem
+        logging.info(f"Loading {tool_name} from tools/{tool_name}.py.")
+        tool_module = importlib.import_module(f"tools.{tool_name}")
+        for name, obj in inspect.getmembers(tool_module):
+            if inspect.isclass(obj):
+                globals()[f"autogen_{name}"] = create_autogen_tool_function(name)
+    tools_module = None  # Already processed
 
-# Create autogen_TOOL_NAME_HERE function for each tool
-if tools_module is not None:
-    for name, obj in inspect.getmembers(tools_module):
-        if inspect.isclass(obj):
-            globals()[f"autogen_{name}"] = create_autogen_tool_function(name)
🧰 Tools
🪛 Ruff (0.15.13)

[error] 59-59: Undefined name tool_name

(F821)


[error] 59-59: Undefined name tool_name

(F821)


[error] 60-60: Undefined name tool_name

(F821)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/inbuilt_tools/autogen_tools.py` around lines 55 - 66, The code uses
an undefined variable tool_name when importing from the tools directory; fix by
iterating over the tool files in tools_dir_path, import each module (e.g.,
importlib.import_module(f"tools.{module_name}") for each .py file excluding
__init__.py), and then inspect that module to register autogen functions using
create_autogen_tool_function for each class found; ensure tools_py_path branch
still loads the single tools module into tools_module and keep the existing loop
over inspect.getmembers(tools_module) to populate globals() with
autogen_{ClassName}.

Comment thread praisonai/ui/chat.py Outdated
Comment on lines +252 to +262
username = os.getenv("CHAINLIT_USERNAME", "admin") # Default to "admin" if not found
password = os.getenv("CHAINLIT_PASSWORD", "admin") # Default to "admin" if not found

@cl.password_auth_callback
def auth_callback(username: str, password: str):
if (username, password) == (username, password):
return cl.User(
identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
)
else:
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Authentication bypass: credential check always passes.

The comparison (username, password) == (username, password) compares the function parameters to themselves, which is always True. The module-level username and password variables (lines 252-253) are shadowed by the function parameters.

🐛 Proposed fix
-username = os.getenv("CHAINLIT_USERNAME", "admin")  # Default to "admin" if not found
-password = os.getenv("CHAINLIT_PASSWORD", "admin")  # Default to "admin" if not found
+expected_username = os.getenv("CHAINLIT_USERNAME", "admin")
+expected_password = os.getenv("CHAINLIT_PASSWORD", "admin")
 
 `@cl.password_auth_callback`
 def auth_callback(username: str, password: str):
-    if (username, password) == (username, password):
+    if (username, password) == (expected_username, expected_password):
         return cl.User(
             identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
         )
     else:
         return None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/ui/chat.py` around lines 252 - 262, The auth check in auth_callback
is incorrectly comparing the function parameters to themselves causing a bypass;
change the module-level credential variable names (e.g., expected_username and
expected_password) or reference them explicitly so the comparison uses the
stored env values, and update auth_callback to compare (username, password)
against (expected_username, expected_password) and return cl.User(...) on match
(keep the metadata), otherwise return None; ensure the module-level variables
currently named username and password are renamed or referenced to avoid
shadowing the auth_callback parameters.

Comment thread praisonai/ui/code.py Outdated
Comment on lines +266 to +276
username = os.getenv("CHAINLIT_USERNAME", "admin") # Default to "admin" if not found
password = os.getenv("CHAINLIT_PASSWORD", "admin") # Default to "admin" if not found

@cl.password_auth_callback
def auth_callback(username: str, password: str):
if (username, password) == (username, password):
return cl.User(
identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
)
else:
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Authentication bypass: credential check always passes.

Same issue as in chat.py - the comparison (username, password) == (username, password) compares function parameters to themselves.

🐛 Proposed fix
-username = os.getenv("CHAINLIT_USERNAME", "admin")
-password = os.getenv("CHAINLIT_PASSWORD", "admin")
+expected_username = os.getenv("CHAINLIT_USERNAME", "admin")
+expected_password = os.getenv("CHAINLIT_PASSWORD", "admin")
 
 `@cl.password_auth_callback`
 def auth_callback(username: str, password: str):
-    if (username, password) == (username, password):
+    if (username, password) == (expected_username, expected_password):
         return cl.User(
             identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
         )
     else:
         return None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
username = os.getenv("CHAINLIT_USERNAME", "admin") # Default to "admin" if not found
password = os.getenv("CHAINLIT_PASSWORD", "admin") # Default to "admin" if not found
@cl.password_auth_callback
def auth_callback(username: str, password: str):
if (username, password) == (username, password):
return cl.User(
identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
)
else:
return None
expected_username = os.getenv("CHAINLIT_USERNAME", "admin") # Default to "admin" if not found
expected_password = os.getenv("CHAINLIT_PASSWORD", "admin") # Default to "admin" if not found
`@cl.password_auth_callback`
def auth_callback(username: str, password: str):
if (username, password) == (expected_username, expected_password):
return cl.User(
identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
)
else:
return None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/ui/code.py` around lines 266 - 276, The auth_callback currently
compares the function parameters to themselves causing an authentication bypass;
update it to compare the incoming username/password parameters against the
stored credentials read from the environment variables (the earlier module-level
variables username and password are shadowing the parameters), e.g., rename the
env vars to something like env_username/env_password or reference them
explicitly, and in auth_callback check (username, password) == (env_username,
env_password); on match return cl.User(identifier=username,
metadata={"role":"ADMIN","provider":"credentials"}) otherwise return None.

Comment thread praisonai/ui/sql_alchemy.py Outdated
Comment on lines +51 to +56
if ssl_require:
# Create an SSL context to require an SSL connection
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
ssl_args["ssl"] = ssl_context

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's verify the exact code at the specified lines
cat -n praisonai/ui/sql_alchemy.py | sed -n '45,60p'

Repository: MervinPraison/PraisonAI

Length of output: 899


🏁 Script executed:

# Let's also check the full context of this function to understand ssl_require
cat -n praisonai/ui/sql_alchemy.py | sed -n '1,80p'

Repository: MervinPraison/PraisonAI

Length of output: 3344


🏁 Script executed:

# Search for ssl_require definition and other SSL configurations in the file
rg -n "ssl_require|ssl_context|CERT_" praisonai/ui/sql_alchemy.py -A 2 -B 2

Repository: MervinPraison/PraisonAI

Length of output: 750


TLS certificate validation is disabled, creating a man-in-the-middle vulnerability when ssl_require=True.

While TLS encryption is still active, setting ssl_context.verify_mode = ssl.CERT_NONE (line 55) and ssl_context.check_hostname = False (line 54) disables certificate verification, removing peer authentication. This allows attackers to intercept connections without detection. If SSL is required, certificate validation should be enabled (e.g., ssl.CERT_REQUIRED with proper CA configuration) or removed entirely if not needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/ui/sql_alchemy.py` around lines 51 - 56, The code currently
disables TLS verification when ssl_require is true by setting
ssl_context.check_hostname = False and ssl_context.verify_mode = ssl.CERT_NONE;
change this to enforce verification: create or configure ssl_context in the
ssl_require branch to use ssl.CERT_REQUIRED and enable hostname checks
(ssl_context.verify_mode = ssl.CERT_REQUIRED and ssl_context.check_hostname =
True), and ensure the context loads trusted CAs (either via
ssl.create_default_context() defaults or by calling
ssl_context.load_verify_locations/cafile if a custom CA bundle is provided);
update the logic that populates ssl_args["ssl"] (the ssl_context variable) so
callers can optionally pass a ca_file/ca_path to use for verification instead of
disabling checks.

Comment thread praisonai/ui/sql_alchemy.py Outdated
Comment on lines +113 to +156
async def get_user(self, identifier: str) -> Optional[PersistedUser]:
logger.debug(f"Getting user: {identifier}")
return cl.PersistedUser(id="test", createdAt=now, identifier=identifier)
if self.show_logger:
logger.info(f"SQLAlchemy: get_user, identifier={identifier}")
query = "SELECT * FROM users WHERE identifier = :identifier"
parameters = {"identifier": identifier}
result = await self.execute_sql(query=query, parameters=parameters)
if result and isinstance(result, list):
user_data = result[0]
return PersistedUser(**user_data)
return None

async def create_user(self, user: User) -> Optional[PersistedUser]:
logger.debug(f"Creating user: {user.identifier}")
return cl.PersistedUser(id="test", createdAt=now, identifier=user.identifier)
if self.show_logger:
logger.info(f"SQLAlchemy: create_user, user_identifier={user.identifier}")
existing_user: Optional["PersistedUser"] = await self.get_user(user.identifier)
user_dict: Dict[str, Any] = {
"identifier": str(user.identifier),
"metadata": json.dumps(user.metadata) or {},
}
if not existing_user: # create the user
if self.show_logger:
logger.info("SQLAlchemy: create_user, creating the user")
user_dict["id"] = str(uuid.uuid4())
user_dict["createdAt"] = await self.get_current_timestamp()
query = """INSERT INTO users ("id", "identifier", "createdAt", "metadata") VALUES (:id, :identifier, :createdAt, :metadata)"""
await self.execute_sql(query=query, parameters=user_dict)
else: # update the user
if self.show_logger:
logger.info("SQLAlchemy: update user metadata")
query = """UPDATE users SET "metadata" = :metadata WHERE "identifier" = :identifier"""
await self.execute_sql(
query=query, parameters=user_dict
) # We want to update the metadata
return await self.get_user(user.identifier)

###### Threads ######
async def get_thread_author(self, thread_id: str) -> str:
logger.debug(f"Getting thread author: {thread_id}")
return "admin"
if self.show_logger:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -type f -name "sql_alchemy.py" 2>/dev/null | head -5

Repository: MervinPraison/PraisonAI

Length of output: 49


🏁 Script executed:

find . -type f -name "sql_alchemy.py" 2>/dev/null

Repository: MervinPraison/PraisonAI

Length of output: 97


🏁 Script executed:

fd -t f "sql_alchemy.py"

Repository: MervinPraison/PraisonAI

Length of output: 95


🏁 Script executed:

sed -n '113,156p' praisonai/ui/sql_alchemy.py

Repository: MervinPraison/PraisonAI

Length of output: 2393


🏁 Script executed:

head -50 praisonai/ui/sql_alchemy.py | cat -n

Repository: MervinPraison/PraisonAI

Length of output: 1864


🏁 Script executed:

sed -n '100,170p' praisonai/ui/sql_alchemy.py | cat -n

Repository: MervinPraison/PraisonAI

Length of output: 4159


Remove hardcoded early returns that bypass all persistence and authorization logic.

Line 116 in get_user, line 129 in create_user, and line 156 in get_thread_author contain early returns that prevent execution of all subsequent SQL queries and business logic. This completely breaks user persistence—users are never actually stored or retrieved from the database. More critically, get_thread_author always returns "admin" regardless of the actual thread owner, circumventing authorization checks that depend on thread ownership verification.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@praisonai/ui/sql_alchemy.py` around lines 113 - 156, Remove the hardcoded
early return stubs in get_user, create_user, and get_thread_author (the
immediate returns returning cl.PersistedUser(...) and "admin") so the functions
execute their real persistence and authorization logic; ensure get_user uses
execute_sql and returns PersistedUser constructed from result (or None),
create_user runs the existing existence check/INSERT or UPDATE flow and then
returns await self.get_user(user.identifier), and get_thread_author performs its
SQL lookup/authorization logic instead of always returning "admin" (use
execute_sql/get_thread_author query and respect returned owner). Also confirm
any referenced symbols used after the removed returns (now, PersistedUser,
execute_sql, get_current_timestamp) are available/imported.

@MervinPraison

Copy link
Copy Markdown
Owner

@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions

Phase 2: FIX Valid Issues
4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
5. Push all code fixes directly to THIS branch (do NOT create a new PR)
6. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
7. If all issues are resolved, approve the PR / close the Issue
8. If blocking issues remain, request changes / leave clear action items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants