Skip to content

Conversation

@rasulkireev
Copy link
Owner

@rasulkireev rasulkireev commented Nov 13, 2025

Summary by CodeRabbit

  • New Features

    • Automated insertion of contextual internal links into generated blog posts
    • AI-driven content validation with structured issue tracking and retry/fix flow
  • Bug Fixes

    • More reliable content quality checks (length, placeholders, endings) and clearer validation messages
  • Refactor

    • Consolidated generation, validation, and linking workflow for smoother content creation and fixes

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Consolidates blog-post validation into a new AI-driven validator, adds an internal-links insertion agent, updates agent imports and model defaults, adjusts system prompts, updates schemas and models to track validation state, and applies a database migration to replace legacy validation fields.

Changes

Cohort / File(s) Summary
Agent Factory Exports
core/agents/__init__.py
Replace create_validate_blog_post_ending_agent with create_validate_blog_post_agent; add create_insert_internal_links_agent to public exports and import new factory.
New Validation Agent
core/agents/validate_blog_post_agent.py
Add BlogPostValidationResult model and create_validate_blog_post_agent() factory for comprehensive validation (completeness, length, placeholders, structure).
New Internal Links Agent
core/agents/insert_internal_links_agent.py
Add create_insert_internal_links_agent() factory configured to insert contextual internal links using must-use and optional page contexts.
Removed Ending Validation Agent
core/agents/validate_blog_post_ending_agent.py
Module deleted; old create_validate_blog_post_ending_agent() removed.
Schema Updates
core/agents/schemas.py
Remove project_pages from BlogPostGenerationContext; add InsertInternalLinksContext and InsertedLinksOutput; add project_pages default_factory to CompetitorVsPostContext; add previous_validation_issues to blog context.
Generate Content Agent
core/agents/generate_blog_post_content_agent.py
Adjust system prompts (remove project pages injection, add validation feedback), lower temperature and set thinking budget; docstring updated to note links inserted later.
System Prompts
core/agents/system_prompts.py
Minor wording/format fixes, condense some prompts to single lines, and add add_validation_feedback(ctx) to inject prior validation issues when present.
Agent Model Defaults & Imports
core/agents/*
Many agents updated to import get_default_ai_model and/or AIModel from core.agents.models instead of core.choices; several agents add model_settings with temperature and thinking_budget: 0.
Agent Models Definition
core/agents/models.py
Add AIModel TextChoices, DEFAULT_AI_MODEL, and get_default_ai_model() as centralized model defaults.
Models & Core Logic
core/models.py
Add is_content_valid, validation_issues, validation_attempts to GeneratedBlogPost; add run_validation(), fix_generated_blog_post(), insert_internal_links() flow and retry logic; wire in create_insert_internal_links_agent and InsertInternalLinksContext.
Database Migration
core/migrations/0043_remove_generatedblogpost_content_too_short_and_more.py
Remove fields content_too_short, has_valid_ending, placeholders, starts_with_header; add is_content_valid (Bool), validation_attempts (Int), validation_issues (JSON/list).
Template Validation Display
frontend/templates/components/blog_post_validation_warning.html
Replace discrete boolean checks with iteration over generated_post.validation_issues to render validation messages.
Settings
tuxseo/settings.py
Remove debug=DEBUG argument from Sentry init() call.
Changelog
CHANGELOG.md
Add top entry for v0.0.8 (2025-11-14) noting "Disabled thinking for Google Models."

Sequence Diagram(s)

sequenceDiagram
    participant Flow as BlogPostFlow
    participant Content as ContentGenAgent
    participant Validator as ValidateAgent
    participant Inserter as InsertLinksAgent
    participant DB as Database

    Flow->>Content: Request generated content (no internal links injected)
    Content-->>Flow: Raw content

    Flow->>Validator: Validate content (completeness, length, placeholders, structure)
    alt Validation passes
        Validator-->>Flow: is_valid=true, issues=[]
        Flow->>Inserter: Insert internal links (must-use & optional pages)
        Inserter-->>Flow: Content with links
        Flow->>DB: Save post (is_content_valid=true)
        DB-->>Flow: Saved
    else Validation fails
        Validator-->>Flow: is_valid=false, issues=[...]
        Flow->>DB: Save validation_issues & increment validation_attempts
        DB-->>Flow: Saved
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50–70 minutes

Areas requiring extra attention:

  • core/models.py: new validation/fix/linking control flow, retries, and state changes.
  • core/migrations/0043_...py: confirm data migration and defaults for removed/added fields.
  • core/agents/insert_internal_links_agent.py and validate_blog_post_agent.py: prompts and output schemas correctness.
  • core/agents/models.py & core/choices.py changes: centralized model selection and removal from core/choices—verify all import updates.

Possibly related PRs

Poem

🐰 I hopped through prompts and stitched a thread,

agents now check what once was spread,
links tucked in where pages belong,
endings fixed and issues gone,
nibble, nudge — the posts grow strong. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.62% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Link insertion' directly corresponds to the primary feature added in this changeset: a new insert_internal_links_agent module that implements AI-driven internal link insertion for blog posts.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch link-insertion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@code-review-doctor code-review-doctor bot left a comment

Choose a reason for hiding this comment

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

Some food for thought. View full project report here.

Comment on lines +753 to 759
logger.info(
"[Generate Content] Attempt %s/%s",
attempt,
MAX_ATTEMPTS,
project_id=self.project.id,
title=self.title,
)

Choose a reason for hiding this comment

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

Suggested change
logger.info(
"[Generate Content] Attempt %s/%s",
attempt,
MAX_ATTEMPTS,
project_id=self.project.id,
title=self.title,
)
logger.info(
"[Generate Content] Attempt %s/%s",
attempt,
MAX_ATTEMPTS,
project_id=self.project_id,
title=self.title,
)

"[Generate Content] Attempt %s/%s",
attempt,
MAX_ATTEMPTS,
project_id=self.project.id,

Choose a reason for hiding this comment

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

self.project.id performs a database read when id is evaluated. You could take advantage of Django's caching of related fields by using self.project_id, which does not do a database read. More info.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 13, 2025

Greptile Overview

Greptile Summary

This PR implements a two-phase approach to blog post generation: first generating content without links, then inserting internal links in a separate step using semantic search.

Key changes:

  • Separated link insertion from content generation for better control and quality
  • Replaced 4 boolean validation fields with a consolidated is_content_valid field and validation_issues list
  • Added automatic retry mechanism (up to 2 attempts) when validation fails
  • Implemented vector similarity search to find contextually relevant pages for linking
  • New insert_internal_links_agent that intelligently places links using must-use and optional page lists
  • Replaced narrow validate_blog_post_ending_agent with comprehensive validate_blog_post_agent that checks completeness, length, placeholders, structure, and quality

Technical improvements:

  • Reduced AI temperature from 0.8 to 0.6 for more consistent content generation
  • Used CosineDistance for semantic similarity matching to find top 5 relevant pages
  • Simplified system prompts and fixed typo in validation message

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended
  • The refactoring is well-structured with clear separation of concerns and proper error handling. Two style violations exist (catching bare Exception), but these are non-critical improvements. The migration is properly generated and the logic is sound.
  • Pay attention to core/models.py - contains the main refactoring logic and two minor style issues with exception handling

Important Files Changed

File Analysis

Filename Score Overview
core/agents/insert_internal_links_agent.py 5/5 New agent that inserts internal links into blog post content, with must-use and optional page lists
core/agents/schemas.py 5/5 Removed project_pages from BlogPostGenerationContext, added InsertInternalLinksContext and InsertedLinksOutput schemas
core/agents/validate_blog_post_agent.py 5/5 New comprehensive validation agent that checks completeness, length, placeholders, structure, and quality
core/migrations/0043_remove_generatedblogpost_content_too_short_and_more.py 5/5 Migration consolidates 4 validation fields into is_content_valid, validation_issues, and validation_attempts
core/models.py 4/5 Major refactoring: consolidated validation logic, added retry mechanism with MAX_ATTEMPTS=2, implemented insert_internal_links method with semantic search

Sequence Diagram

sequenceDiagram
    participant User
    participant BlogPostTitleSuggestion
    participant GenerateContentAgent
    participant GeneratedBlogPost
    participant ValidationAgent
    participant InsertLinksAgent
    participant ProjectPage

    User->>BlogPostTitleSuggestion: generate_content()
    
    loop MAX_ATTEMPTS=2
        BlogPostTitleSuggestion->>GenerateContentAgent: Create agent & generate content
        Note over GenerateContentAgent: No project_pages in context<br/>Temperature: 0.6
        GenerateContentAgent-->>BlogPostTitleSuggestion: Blog post content (no links)
        
        alt First attempt
            BlogPostTitleSuggestion->>GeneratedBlogPost: create_and_validate()
        else Retry
            BlogPostTitleSuggestion->>GeneratedBlogPost: Update content & save
        end
        
        GeneratedBlogPost->>ValidationAgent: run_validation()
        ValidationAgent->>ValidationAgent: Check completeness, length,<br/>placeholders, structure, quality
        ValidationAgent-->>GeneratedBlogPost: is_valid, validation_issues
        
        alt Content is valid
            GeneratedBlogPost->>InsertLinksAgent: insert_internal_links()
            GeneratedBlogPost->>ProjectPage: Get must_use_pages (always_use=True)
            ProjectPage-->>GeneratedBlogPost: Must-use pages
            GeneratedBlogPost->>ProjectPage: Vector similarity search<br/>(top 5 similar pages)
            ProjectPage-->>GeneratedBlogPost: Optional pages
            GeneratedBlogPost->>InsertLinksAgent: Pass content + page contexts
            InsertLinksAgent->>InsertLinksAgent: Insert links with<br/>contextual relevance
            InsertLinksAgent-->>GeneratedBlogPost: Content with internal links
            GeneratedBlogPost->>GeneratedBlogPost: save(update_fields=['content'])
            GeneratedBlogPost-->>BlogPostTitleSuggestion: Return valid blog post
        else Content invalid & attempts < MAX_ATTEMPTS
            Note over BlogPostTitleSuggestion: Retry generation
        else Content invalid & attempts == MAX_ATTEMPTS
            GeneratedBlogPost-->>BlogPostTitleSuggestion: Return invalid blog post
        end
    end
    
    BlogPostTitleSuggestion-->>User: Generated blog post
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

11 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile


return ending_is_valid

except Exception as error:
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Catching bare Exception violates the backend error handling style guide

Suggested change
except Exception as error:
except (PydanticAIError, ValueError) as error:

Context Used: Context from dashboard - .cursor/rules/backend-error-handling.mdc (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: core/models.py
Line: 984:984

Comment:
**style:** Catching bare `Exception` violates the backend error handling style guide

```suggestion
        except (PydanticAIError, ValueError) as error:
```

**Context Used:** Context from `dashboard` - .cursor/rules/backend-error-handling.mdc ([source](https://app.greptile.com/review/custom-context?memory=9343d853-8bca-46ca-b37d-fd4327d3e3d2))

How can I resolve this? If you propose a fix, please make it concise.

self.fix_header_start()
return True

except Exception as error:
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Catching bare Exception violates the backend error handling style guide

Suggested change
except Exception as error:
except (PydanticAIError, ValueError) as error:

Context Used: Context from dashboard - .cursor/rules/backend-error-handling.mdc (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: core/models.py
Line: 1216:1216

Comment:
**style:** Catching bare `Exception` violates the backend error handling style guide

```suggestion
        except (PydanticAIError, ValueError) as error:
```

**Context Used:** Context from `dashboard` - .cursor/rules/backend-error-handling.mdc ([source](https://app.greptile.com/review/custom-context?memory=9343d853-8bca-46ca-b37d-fd4327d3e3d2))

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
core/agents/system_prompts.py (1)

110-119: Fix invalid multi-line f-string expressions.

The new f-string inserts newline characters inside the {…} expression, which Python rejects (SyntaxError: f-string expression part cannot include a newline). Module import will fail until this is corrected. Refactor to compute the values first (or keep the expression on a single line) before interpolating them.

-    return f"""
+    target_keywords = (
+        ", ".join(title.target_keywords) if title.target_keywords else "None specified"
+    )
+    suggested_meta_description = (
+        title.suggested_meta_description or "None specified"
+    )
+    return f"""
         This is the title suggestion generated by AI using project information:
         - Title: {title.title}
         - Description: {title.description}
         - Category: {title.category}
-        - Target Keywords: {
-        ", ".join(title.target_keywords) if title.target_keywords else "None specified"
-    }
-        - Suggested Meta Description: {
-        title.suggested_meta_description if title.suggested_meta_description else "None specified"
-    }
+        - Target Keywords: {target_keywords}
+        - Suggested Meta Description: {suggested_meta_description}
     """
🧹 Nitpick comments (3)
core/agents/validate_blog_post_agent.py (1)

1-9: Use Field(default_factory=list) for mutable default.

Avoid sharing the same list instance across models by switching to Field(default_factory=list) and importing Field. Keeps the schema safer and aligns with our Pydantic patterns.

-from pydantic import BaseModel
+from pydantic import BaseModel, Field
@@
-class BlogPostValidationResult(BaseModel):
-    is_valid: bool
-    issues: list[str] = []
+class BlogPostValidationResult(BaseModel):
+    is_valid: bool
+    issues: list[str] = Field(default_factory=list)
core/agents/insert_internal_links_agent.py (1)

51-51: Remove unused # noqa: E501.

Ruff reports this suppression as unused; please drop it to keep lint clean.

core/models.py (1)

1197-1197: Remove unused # noqa: E501.

This suppression is redundant under the current lint config; please drop it for cleanliness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cab5af and 86f4209.

📒 Files selected for processing (11)
  • core/agents/__init__.py (2 hunks)
  • core/agents/generate_blog_post_content_agent.py (2 hunks)
  • core/agents/insert_internal_links_agent.py (1 hunks)
  • core/agents/schemas.py (1 hunks)
  • core/agents/system_prompts.py (3 hunks)
  • core/agents/validate_blog_post_agent.py (1 hunks)
  • core/agents/validate_blog_post_ending_agent.py (0 hunks)
  • core/migrations/0043_remove_generatedblogpost_content_too_short_and_more.py (1 hunks)
  • core/models.py (6 hunks)
  • frontend/templates/components/blog_post_validation_warning.html (1 hunks)
  • tuxseo/settings.py (0 hunks)
💤 Files with no reviewable changes (2)
  • tuxseo/settings.py
  • core/agents/validate_blog_post_ending_agent.py
🧰 Additional context used
📓 Path-based instructions (8)
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend.mdc)

**/*.py: Prioritize readability and maintainability; follow PEP 8
Use descriptive variable/function names with underscores

In Python, use try-except blocks that catch specific exception types; do not catch broad Exception.

**/*.py: Follow PEP 8 for Python code style
Use descriptive variable names with underscores (snake_case) in Python
Prefer Django's built-in features over external libraries

**/*.py: Use structlog for logging in Python code (avoid print and the standard logging module)
Include contextual key-value fields in log messages (e.g., error=str(e), exc_info=True, profile_id=profile.id)

**/*.py: Use descriptive, full-word variable names; avoid abbreviations and single-letter variables in Python
Provide context in variable names when format or type matters (e.g., include _iso_format, date)
Extract unchanging values into UPPER_CASE constants
Use intermediary variables to name parsed groups instead of using index access directly
Naming conventions: use is_/has_/can_ for booleans; include 'date' for dates; snake_case for variables/functions; PascalCase for classes
Define variables close to where they are used to keep lifespan short
Name things after what they do, not how they're used; ensure names make sense without extra context
Avoid generic names like data, info, manager; use specific, intention-revealing names
Function names should include necessary context without being verbose
If naming is hard, split functions into smaller focused parts
Maintain consistency: reuse the same verbs and nouns for the same concepts; name variables after the functions that create them
Use more descriptive names for longer-lived variables
Avoid else statements by using guard clauses for early returns
Replace simple conditionals with direct assignment when both branches call the same function with different values
Use dictionaries as dispatch tables instead of multiple equal-probability elif chains
Validate input before processing to prevent errors propagating in...

Files:

  • core/agents/__init__.py
  • core/agents/insert_internal_links_agent.py
  • core/agents/schemas.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/models.py
  • core/migrations/0043_remove_generatedblogpost_content_too_short_and_more.py
  • core/agents/system_prompts.py
core/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend.mdc)

Leverage Django's ORM; avoid raw SQL when possible

Files:

  • core/agents/__init__.py
  • core/agents/insert_internal_links_agent.py
  • core/agents/schemas.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/models.py
  • core/migrations/0043_remove_generatedblogpost_content_too_short_and_more.py
  • core/agents/system_prompts.py
core/agents/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement AI agents using pydantic-ai under core/agents/

Files:

  • core/agents/__init__.py
  • core/agents/insert_internal_links_agent.py
  • core/agents/schemas.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/agents/system_prompts.py
**/*.html

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

**/*.html: Prefer Stimulus JS for adding interactivity to Django templates instead of raw script elements
Leverage Stimulus data attributes to connect HTML elements with JavaScript functionality
Utilize Stimulus targets to reference specific elements within a controller
Employ Stimulus actions to handle user interactions and events

Files:

  • frontend/templates/components/blog_post_validation_warning.html
**/*.{html,htm}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

Always generate semantic HTML

Files:

  • frontend/templates/components/blog_post_validation_warning.html
{**/*.{html,htm,css,scss},**/*_controller.@(js|ts)}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

Always favor the utility-first Tailwind approach; avoid creating reusable style classes and prefer reuse via template components

Files:

  • frontend/templates/components/blog_post_validation_warning.html
frontend/templates/**/*.html

📄 CodeRabbit inference engine (.cursor/rules/stimulus-events.mdc)

Avoid data-*-outlet links for sibling controller communication when using the event-based approach; keep controllers self-contained

Use semantic HTML elements (e.g., dialog, details/summary) in templates

Files:

  • frontend/templates/components/blog_post_validation_warning.html
core/models.py

📄 CodeRabbit inference engine (CLAUDE.md)

core/models.py: Place business logic in Django models (fat models pattern)
Validate simple constraints in the database and place complex domain logic in Django models

Files:

  • core/models.py
🧠 Learnings (2)
📚 Learning: 2025-10-04T08:52:37.437Z
Learnt from: CR
Repo: rasulkireev/TuxSEO PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T08:52:37.437Z
Learning: Always add AGENTS.md into AI context

Applied to files:

  • core/agents/__init__.py
  • core/agents/insert_internal_links_agent.py
📚 Learning: 2025-10-04T08:52:37.437Z
Learnt from: CR
Repo: rasulkireev/TuxSEO PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T08:52:37.437Z
Learning: Applies to core/agents/**/*.py : Implement AI agents using pydantic-ai under core/agents/

Applied to files:

  • core/agents/validate_blog_post_agent.py
🧬 Code graph analysis (4)
core/agents/__init__.py (2)
core/agents/insert_internal_links_agent.py (1)
  • create_insert_internal_links_agent (7-85)
core/agents/validate_blog_post_agent.py (1)
  • create_validate_blog_post_agent (12-43)
core/agents/insert_internal_links_agent.py (2)
core/agents/schemas.py (2)
  • InsertedLinksOutput (293-298)
  • InsertInternalLinksContext (279-290)
core/choices.py (1)
  • get_default_ai_model (136-138)
core/agents/validate_blog_post_agent.py (1)
core/choices.py (1)
  • get_default_ai_model (136-138)
core/models.py (7)
core/agents/insert_internal_links_agent.py (2)
  • create_insert_internal_links_agent (7-85)
  • output (82-83)
core/agents/schemas.py (4)
  • BlogPostGenerationContext (182-188)
  • GeneratedBlogPostSchema (191-201)
  • InsertInternalLinksContext (279-290)
  • ProjectPageContext (171-179)
core/agents/generate_blog_post_content_agent.py (1)
  • create_generate_blog_post_content_agent (19-54)
core/utils.py (2)
  • run_agent_synchronously (194-251)
  • get_jina_embedding (144-186)
core/agents/validate_blog_post_agent.py (1)
  • create_validate_blog_post_agent (12-43)
core/api/views.py (1)
  • fix_generated_blog_post (1081-1111)
core/agents/content_editor_agent.py (1)
  • create_content_editor_agent (14-49)
🪛 Ruff (0.14.4)
core/agents/insert_internal_links_agent.py

51-51: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

core/agents/validate_blog_post_agent.py

38-38: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

core/models.py

992-992: Use explicit conversion flag

Replace with conversion flag

(RUF010)


1197-1197: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)


1214-1214: Consider moving this statement to an else block

(TRY300)

core/migrations/0043_remove_generatedblogpost_content_too_short_and_more.py

8-10: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


12-44: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

core/agents/system_prompts.py

22-22: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

Comment on lines +936 to +942
base_logger_info = {
"blog_post_id": self.id,
"project_id": self.project_id,
"project_name": self.project.name,
"profile_id": self.project.profile.id,
"profile_email": self.project.profile.user.email,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard validation logging against missing relations.

GeneratedBlogPost.project and Project.profile are nullable, so building base_logger_info this way raises AttributeError when either relation is absent, aborting validation. Please derive these IDs/emails defensively (falling back to None) before logging so the validation run can proceed even when data is partially missing.

-        base_logger_info = {
-            "blog_post_id": self.id,
-            "project_id": self.project_id,
-            "project_name": self.project.name,
-            "profile_id": self.project.profile.id,
-            "profile_email": self.project.profile.user.email,
-        }
+        project = self.project
+        profile = project.profile if project else None
+        base_logger_info = {
+            "blog_post_id": self.id,
+            "project_id": project.id if project else None,
+            "project_name": project.name if project else None,
+            "profile_id": profile.id if profile else None,
+            "profile_email": profile.user.email if profile else None,
+        }
🤖 Prompt for AI Agents
In core/models.py around lines 936 to 942, building base_logger_info accesses
project, project.name, project.profile and project.profile.user.email directly
which raises AttributeError when those relations are null; change the
construction to derive values defensively (e.g., use local variables with
conditional access or getattr/defaults) so project_id = self.project_id,
project_name = self.project.name if self.project else None, profile_id =
self.project.profile.id if self.project and self.project.profile else None,
profile_email = self.project.profile.user.email if self.project and
self.project.profile and getattr(self.project.profile, "user", None) else None,
and then use those vars in base_logger_info so validation continues when
relations are missing.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

♻️ Duplicate comments (4)
core/models.py (4)

758-758: Optimize database access by using self.project_id.

Line 758 accesses self.project.id, which triggers a database read. Use self.project_id instead to leverage Django's cached foreign key ID.

As per coding guidelines.


940-946: Guard validation logging against missing relations.

The code directly accesses self.project.name, self.project.profile.id, and self.project.profile.user.email without checking if these relations exist. This will raise AttributeError if project or project.profile is None, preventing validation from running.


988-988: Catch specific exception types instead of bare Exception.

Catching bare Exception violates the error handling guidelines. Use specific exception types like PydanticAIError or ValueError.

As per coding guidelines.


1220-1220: Catch specific exception types instead of bare Exception.

Catching bare Exception violates the error handling guidelines. Consider catching specific exceptions like PydanticAIError or ValueError.

As per coding guidelines.

🧹 Nitpick comments (2)
core/models.py (2)

996-996: Use explicit conversion flag in f-string.

The static analysis tool correctly identifies that validation_issues should use an explicit conversion flag.

Apply this diff:

-            self.validation_issues = [f"Validation error: {str(error)}"]
+            self.validation_issues = [f"Validation error: {error!s}"]

1201-1201: Remove unused noqa directive.

The # noqa: E501 comment is unnecessary as the line length is acceptable or the rule is not enabled.

Apply this diff:

-                "Please insert internal links into the blog post content where contextually relevant.",  # noqa: E501
+                "Please insert internal links into the blog post content where contextually relevant.",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86f4209 and 3f6bf67.

📒 Files selected for processing (19)
  • CHANGELOG.md (1 hunks)
  • core/agents/analyze_competitor_agent.py (2 hunks)
  • core/agents/analyze_project_agent.py (2 hunks)
  • core/agents/competitor_vs_blog_post_agent.py (2 hunks)
  • core/agents/content_editor_agent.py (2 hunks)
  • core/agents/extract_competitors_data_agent.py (2 hunks)
  • core/agents/extract_links_agent.py (2 hunks)
  • core/agents/find_competitors_agent.py (2 hunks)
  • core/agents/generate_blog_post_content_agent.py (3 hunks)
  • core/agents/insert_internal_links_agent.py (1 hunks)
  • core/agents/models.py (1 hunks)
  • core/agents/populate_competitor_details_agent.py (2 hunks)
  • core/agents/schemas.py (2 hunks)
  • core/agents/summarize_page_agent.py (2 hunks)
  • core/agents/system_prompts.py (4 hunks)
  • core/agents/title_suggestions_agent.py (3 hunks)
  • core/agents/validate_blog_post_agent.py (1 hunks)
  • core/choices.py (0 hunks)
  • core/models.py (6 hunks)
💤 Files with no reviewable changes (1)
  • core/choices.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend.mdc)

**/*.py: Prioritize readability and maintainability; follow PEP 8
Use descriptive variable/function names with underscores

In Python, use try-except blocks that catch specific exception types; do not catch broad Exception.

**/*.py: Follow PEP 8 for Python code style
Use descriptive variable names with underscores (snake_case) in Python
Prefer Django's built-in features over external libraries

**/*.py: Use structlog for logging in Python code (avoid print and the standard logging module)
Include contextual key-value fields in log messages (e.g., error=str(e), exc_info=True, profile_id=profile.id)

**/*.py: Use descriptive, full-word variable names; avoid abbreviations and single-letter variables in Python
Provide context in variable names when format or type matters (e.g., include _iso_format, date)
Extract unchanging values into UPPER_CASE constants
Use intermediary variables to name parsed groups instead of using index access directly
Naming conventions: use is_/has_/can_ for booleans; include 'date' for dates; snake_case for variables/functions; PascalCase for classes
Define variables close to where they are used to keep lifespan short
Name things after what they do, not how they're used; ensure names make sense without extra context
Avoid generic names like data, info, manager; use specific, intention-revealing names
Function names should include necessary context without being verbose
If naming is hard, split functions into smaller focused parts
Maintain consistency: reuse the same verbs and nouns for the same concepts; name variables after the functions that create them
Use more descriptive names for longer-lived variables
Avoid else statements by using guard clauses for early returns
Replace simple conditionals with direct assignment when both branches call the same function with different values
Use dictionaries as dispatch tables instead of multiple equal-probability elif chains
Validate input before processing to prevent errors propagating in...

Files:

  • core/agents/summarize_page_agent.py
  • core/agents/analyze_project_agent.py
  • core/agents/models.py
  • core/agents/populate_competitor_details_agent.py
  • core/agents/content_editor_agent.py
  • core/agents/find_competitors_agent.py
  • core/agents/analyze_competitor_agent.py
  • core/models.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/schemas.py
  • core/agents/title_suggestions_agent.py
  • core/agents/extract_competitors_data_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/agents/competitor_vs_blog_post_agent.py
  • core/agents/system_prompts.py
  • core/agents/extract_links_agent.py
  • core/agents/insert_internal_links_agent.py
core/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend.mdc)

Leverage Django's ORM; avoid raw SQL when possible

Files:

  • core/agents/summarize_page_agent.py
  • core/agents/analyze_project_agent.py
  • core/agents/models.py
  • core/agents/populate_competitor_details_agent.py
  • core/agents/content_editor_agent.py
  • core/agents/find_competitors_agent.py
  • core/agents/analyze_competitor_agent.py
  • core/models.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/schemas.py
  • core/agents/title_suggestions_agent.py
  • core/agents/extract_competitors_data_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/agents/competitor_vs_blog_post_agent.py
  • core/agents/system_prompts.py
  • core/agents/extract_links_agent.py
  • core/agents/insert_internal_links_agent.py
core/agents/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement AI agents using pydantic-ai under core/agents/

Files:

  • core/agents/summarize_page_agent.py
  • core/agents/analyze_project_agent.py
  • core/agents/models.py
  • core/agents/populate_competitor_details_agent.py
  • core/agents/content_editor_agent.py
  • core/agents/find_competitors_agent.py
  • core/agents/analyze_competitor_agent.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/schemas.py
  • core/agents/title_suggestions_agent.py
  • core/agents/extract_competitors_data_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/agents/competitor_vs_blog_post_agent.py
  • core/agents/system_prompts.py
  • core/agents/extract_links_agent.py
  • core/agents/insert_internal_links_agent.py
core/models.py

📄 CodeRabbit inference engine (CLAUDE.md)

core/models.py: Place business logic in Django models (fat models pattern)
Validate simple constraints in the database and place complex domain logic in Django models

Files:

  • core/models.py
🧠 Learnings (3)
📚 Learning: 2025-10-04T08:52:58.590Z
Learnt from: CR
Repo: rasulkireev/TuxSEO PR: 0
File: .cursor/rules/agent-rules.mdc:0-0
Timestamp: 2025-10-04T08:52:58.590Z
Learning: Always add AGENTS.md into AI context

Applied to files:

  • core/agents/summarize_page_agent.py
  • core/agents/analyze_project_agent.py
  • core/agents/models.py
  • core/agents/content_editor_agent.py
  • core/agents/find_competitors_agent.py
  • core/agents/title_suggestions_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/agents/competitor_vs_blog_post_agent.py
  • core/agents/extract_links_agent.py
📚 Learning: 2025-10-04T08:52:37.437Z
Learnt from: CR
Repo: rasulkireev/TuxSEO PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T08:52:37.437Z
Learning: Applies to core/agents/**/*.py : Implement AI agents using pydantic-ai under core/agents/

Applied to files:

  • core/agents/summarize_page_agent.py
  • core/agents/analyze_project_agent.py
  • core/agents/models.py
  • core/agents/populate_competitor_details_agent.py
  • core/agents/content_editor_agent.py
  • core/agents/find_competitors_agent.py
  • core/agents/analyze_competitor_agent.py
  • core/agents/validate_blog_post_agent.py
  • core/agents/title_suggestions_agent.py
  • core/agents/extract_competitors_data_agent.py
  • core/agents/generate_blog_post_content_agent.py
  • core/agents/competitor_vs_blog_post_agent.py
  • core/agents/extract_links_agent.py
📚 Learning: 2025-10-05T01:51:48.411Z
Learnt from: CR
Repo: rasulkireev/TuxSEO PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-05T01:51:48.411Z
Learning: Applies to **/*.py : Handle errors at the source and return safe defaults instead of propagating exceptions

Applied to files:

  • core/models.py
🧬 Code graph analysis (15)
core/agents/summarize_page_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/analyze_project_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/populate_competitor_details_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/content_editor_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/find_competitors_agent.py (2)
core/agents/models.py (1)
  • AIModel (4-6)
core/agents/schemas.py (1)
  • ProjectDetails (17-99)
core/agents/analyze_competitor_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/models.py (7)
core/agents/insert_internal_links_agent.py (2)
  • create_insert_internal_links_agent (7-85)
  • output (82-83)
core/agents/schemas.py (4)
  • BlogPostGenerationContext (182-192)
  • GeneratedBlogPostSchema (195-205)
  • InsertInternalLinksContext (283-294)
  • ProjectPageContext (171-179)
core/agents/generate_blog_post_content_agent.py (1)
  • create_generate_blog_post_content_agent (21-57)
core/utils.py (2)
  • run_agent_synchronously (194-251)
  • get_jina_embedding (144-186)
core/agents/validate_blog_post_agent.py (1)
  • create_validate_blog_post_agent (12-41)
core/api/views.py (1)
  • fix_generated_blog_post (1081-1111)
core/agents/content_editor_agent.py (1)
  • create_content_editor_agent (14-49)
core/agents/validate_blog_post_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/title_suggestions_agent.py (4)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/schemas.py (1)
  • TitleSuggestionContext (132-148)
core/agents/system_prompts.py (2)
  • add_todays_date (7-8)
  • add_language_specification (123-128)
core/choices.py (1)
  • ContentType (4-6)
core/agents/extract_competitors_data_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/generate_blog_post_content_agent.py (4)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/schemas.py (2)
  • BlogPostGenerationContext (182-192)
  • GeneratedBlogPostSchema (195-205)
core/agents/system_prompts.py (10)
  • add_language_specification (123-128)
  • add_project_details (40-53)
  • add_target_keywords (131-144)
  • add_title_details (107-120)
  • add_todays_date (7-8)
  • add_validation_feedback (156-173)
  • filler_content (27-37)
  • markdown_lists (23-24)
  • post_structure (15-20)
  • valid_markdown_format (11-12)
core/choices.py (1)
  • ContentType (4-6)
core/agents/competitor_vs_blog_post_agent.py (1)
core/agents/models.py (1)
  • AIModel (4-6)
core/agents/system_prompts.py (2)
core/models.py (1)
  • project_details (433-448)
core/agents/schemas.py (1)
  • BlogPostGenerationContext (182-192)
core/agents/extract_links_agent.py (1)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/insert_internal_links_agent.py (2)
core/agents/models.py (1)
  • get_default_ai_model (12-14)
core/agents/schemas.py (2)
  • InsertedLinksOutput (297-302)
  • InsertInternalLinksContext (283-294)
🪛 Ruff (0.14.4)
core/agents/find_competitors_agent.py

73-73: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)


79-79: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

core/models.py

996-996: Use explicit conversion flag

Replace with conversion flag

(RUF010)


1201-1201: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)


1218-1218: Consider moving this statement to an else block

(TRY300)

core/agents/validate_blog_post_agent.py

22-22: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

core/agents/system_prompts.py

20-20: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

core/agents/insert_internal_links_agent.py

51-51: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (35)
core/agents/content_editor_agent.py (2)

3-3: LGTM: Import path updated to centralized location.

The import consolidation to core.agents.models improves maintainability by centralizing AI model selection logic.


40-40: LGTM: Prompt text simplified.

Removing the "IMPORTANT:" prefix simplifies the prompt while maintaining clarity. This change is consistent with similar updates across other agent files.

core/agents/summarize_page_agent.py (1)

3-3: LGTM: Consistent with centralized AI model configuration.

The import path update and model_settings changes align with the broader refactor to centralize AI model configuration. The thinking_budget parameter verification requested in content_editor_agent.py applies here as well.

Also applies to: 28-28

core/agents/analyze_project_agent.py (1)

3-3: LGTM: Consistent configuration updates.

The import consolidation and model_settings configuration are consistent with the broader refactor. The temperature of 0.8 is appropriate for creative analysis tasks.

Also applies to: 28-28

core/agents/analyze_competitor_agent.py (1)

4-4: LGTM: Import path updated to centralized location.

The import consolidation improves code organization.

core/agents/extract_links_agent.py (1)

3-3: LGTM: Appropriate configuration for link extraction.

The import consolidation is consistent with the broader refactor. The low temperature (0.2) is well-suited for deterministic link extraction tasks.

Also applies to: 27-27

core/agents/title_suggestions_agent.py (1)

3-6: LGTM: Consistent refactoring and prompt simplification.

The import consolidation for AI model configuration while maintaining ContentType in core.choices shows appropriate separation of concerns. The removal of "IMPORTANT:" prefixes simplifies the prompts without losing clarity.

Also applies to: 18-18, 41-41, 47-47

core/agents/competitor_vs_blog_post_agent.py (1)

6-6: LGTM: Consistent with centralized configuration.

The import update to use AIModel from core.agents.models aligns with the broader refactor, and the prompt text simplification is consistent with changes across other agent files.

Also applies to: 67-67

core/agents/find_competitors_agent.py (1)

6-6: LGTM: Import path updated correctly.

The import has been correctly updated to use the new centralized location for AI model definitions.

core/agents/generate_blog_post_content_agent.py (4)

3-3: LGTM: Imports updated correctly.

The imports have been properly updated to use the new centralized locations for AI model utilities and the new validation feedback helper.

Also applies to: 11-11


27-29: Good documentation of the two-step process.

This clarification helps developers understand that content generation and internal linking are now separate concerns, which is a sound architectural decision.


51-51: LGTM: Validation feedback integration.

The addition of add_validation_feedback enables the agent to learn from previous validation failures, which should improve content quality on retries.


43-43: Confirm that the temperature reduction to 0.3 aligns with project quality requirements.

This temperature setting is consistent with other content generation agents in the codebase (insert_internal_links_agent and content_editor_agent both use 0.3), while analysis agents intentionally use 0.8. The lower temperature makes output more deterministic, which appears intentional for structured content generation. The agent includes validation feedback mechanisms (retries=2, validation system prompt) to handle this behavior.

However, verify that:

  1. This change was intentional for this PR and not accidental
  2. Quality expectations are still met with reduced creativity/diversity
  3. The validation loop effectively handles deterministic output
core/agents/extract_competitors_data_agent.py (2)

3-3: LGTM: Import path updated correctly.

The import has been correctly updated to use the new centralized location for AI model utilities.


25-25: LGTM: Appropriate model settings for data extraction.

The temperature of 0.2 is well-suited for structured data extraction tasks where deterministic, accurate output is more important than creativity.

core/agents/populate_competitor_details_agent.py (2)

3-3: LGTM: Import path updated correctly.

The import has been correctly updated to use the new centralized location for AI model utilities.


30-30: LGTM: Appropriate model settings for inference tasks.

The temperature of 0.5 strikes a good balance between creativity and consistency for inferring competitor details from webpage content.

core/agents/schemas.py (4)

189-192: LGTM: Well-designed validation feedback mechanism.

The previous_validation_issues field enables the agent to learn from previous failures and improve content quality on retries. The field has a clear description and appropriate default value.


278-280: LGTM: Project pages added for internal linking.

Adding project_pages to CompetitorVsPostContext enables internal linking in competitor comparison posts, maintaining consistency with the broader linking workflow.


283-295: LGTM: Well-structured context for link insertion.

The InsertInternalLinksContext model clearly separates must-use pages from optional pages, enabling intelligent link insertion based on priority and contextual relevance.


297-302: LGTM: Clean output schema.

The InsertedLinksOutput model provides a simple, focused contract for the link insertion agent's output.

core/agents/insert_internal_links_agent.py (3)

7-19: LGTM: Clear function signature and documentation.

The docstring clearly explains the agent's purpose and behavior, making it easy for developers to understand how to use it.


20-51: Excellent system prompt design.

The system prompt provides comprehensive, actionable guidelines for link insertion:

  • Clear quality standards (natural integration, contextual relevance)
  • Specific instructions for anchor text and placement
  • Distinction between must-use and optional pages
  • Preservation of existing content

This should result in high-quality, contextually relevant internal linking.


53-60: LGTM: Appropriate agent configuration.

The temperature of 0.3 and thinking_budget: 0 strike a good balance for link insertion, providing consistency while allowing some flexibility in anchor text selection and placement decisions.

core/agents/system_prompts.py (4)

12-12: LGTM: Simplified prompt functions.

The simplification of valid_markdown_format and markdown_lists to single-line returns improves readability without losing clarity.

Also applies to: 24-24


27-37: LGTM: Clearer filler content guidance.

The explicit examples ("No link placeholders", "No image placeholders") make the instructions more concrete and easier for the AI to follow.


110-110: Good catch: Typo fixed.

Fixed "gnerate" → "generated".


156-173: LGTM: Well-implemented validation feedback mechanism.

The add_validation_feedback function is well-designed:

  • Uses hasattr to safely check for optional field
  • Clearly formats validation issues for AI consumption
  • Provides strong directive to avoid repeating mistakes
  • Returns empty string when no issues exist

This should effectively improve content quality on retries.

core/agents/validate_blog_post_agent.py (3)

7-9: LGTM: Clean validation result model.

The BlogPostValidationResult model provides a simple, focused contract with a boolean validity flag and a list of specific issues.


12-25: LGTM: Appropriate agent configuration for validation.

The extremely low temperature (0.1) and single retry are well-suited for validation tasks where consistency and determinism are critical.


27-39: LGTM: Comprehensive validation checks.

The validation criteria cover the most critical quality issues:

  • Completeness: Ensures content isn't truncated
  • Length: Enforces minimum content threshold (2500-3000 chars)
  • Placeholders: Catches common content generation failures
  • Structure: Ensures proper formatting

These checks should catch the majority of publication-blocking issues.

core/models.py (4)

909-911: Well-designed validation state tracking.

The new validation fields provide a clean, extensible approach to tracking content quality. Using a JSONField for issues allows detailed feedback accumulation.


1077-1117: Well-structured fix workflow.

The method correctly checks validation state before attempting fixes, uses the content editor agent appropriately, and re-validates after fixing. The integration with link insertion when content becomes valid is a logical flow.


1144-1163: Excellent semantic search implementation.

The use of pgvector's CosineDistance for finding semantically similar pages is well-designed. Separating must-use pages from optional pages based on similarity provides good control over link insertion strategy.


805-820: Clean integration of validation, link insertion, and OG image generation.

The flow properly gates link insertion on successful validation, passes through the optional model parameter, and respects project settings for OG image generation. The separation of concerns is well-maintained.

You are an expert marketer.
Based on the competitor details and homepage content provided,
extract and infer the requested information. Make reasonable inferences based
extract and infer the requested information. Make reasonable inferences based
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix extra whitespace in system prompt.

Line 26 contains multiple consecutive spaces: "Make reasonable inferences based". This appears to be a formatting error.

Apply this diff to fix the spacing:

-            extract and infer the requested information. Make     reasonable inferences based
+            extract and infer the requested information. Make reasonable inferences based
📝 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
extract and infer the requested information. Make reasonable inferences based
extract and infer the requested information. Make reasonable inferences based
🤖 Prompt for AI Agents
In core/agents/analyze_competitor_agent.py around line 26, the system prompt
string contains extra consecutive spaces ("Make     reasonable inferences
based"); remove the extra spaces so the sentence uses single spacing ("Make
reasonable inferences based") and ensure surrounding punctuation/indentation
remains unchanged.

""",
retries=2,
model_settings={"temperature": 0.3},
model_settings={"temperature": 0.3, "thinking_budget": 0},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the thinking_budget parameter is supported.

The thinking_budget: 0 parameter has been added to model_settings. Please confirm this is a valid parameter for the version of pydantic-ai in use.


🌐 Web query:

Does pydantic-ai Agent support a thinking_budget parameter in model_settings?

💡 Result:

Yes. Agent accepts model_settings and you can set a thinking_budget via a model-specific settings class — e.g., GoogleModelSettings supports google_thinking_config={'thinking_budget': ...} which can be passed to Agent (or at run time). [1][2]

Sources:
[1] Pydantic AI — Agents (model_settings description).
[2] Pydantic AI — Google model docs (example showing google_thinking_config with thinking_budget).


🏁 Script executed:

#!/bin/bash
# View the content_editor_agent.py file to see full context
cat core/agents/content_editor_agent.py | head -60

Length of output: 1381


🏁 Script executed:

#!/bin/bash
# Search for model initialization patterns in agents directory
rg -nP --type=py -C5 'model_settings.*thinking_budget' core/agents/

Length of output: 8860


🏁 Script executed:

#!/bin/bash
# Check what model is being used in this agent
rg -nP --type=py -C10 'class.*Agent|ClientModel|model=' core/agents/content_editor_agent.py

Length of output: 575


🏁 Script executed:

#!/bin/bash
# Find all Agent instantiations in the agents directory
rg -nP --type=py 'Agent\(' core/agents/ -C3

Length of output: 6583


🏁 Script executed:

#!/bin/bash
# Check what get_default_ai_model returns
cat core/agents/models.py

Length of output: 362


🏁 Script executed:

#!/bin/bash
# Check if there's requirements or setup to see pydantic-ai version
fd -e 'txt' -e 'toml' -e 'cfg' --type f | grep -E '(requirements|setup|pyproject|poetry)' | head -5

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Search for any imports or usage of model settings structures
rg -nP --type=py 'ModelSettings|model_settings' core/agents/ -B2 -A2 | head -50

Length of output: 3016


🏁 Script executed:

#!/bin/bash
# Check pydantic-ai version
grep -A5 -B5 'pydantic.ai\|pydantic-ai' pyproject.toml requirements.txt 2>/dev/null | head -20

Length of output: 1182


🌐 Web query:

pydantic-ai Agent model_settings thinking_budget parameter syntax

💡 Result:

Short answer — use the model-specific settings field (GoogleModelSettings.google_thinking_config) and pass a dict with key "thinking_budget". Example:

  • Enable / set budget:
    from pydantic_ai import Agent
    from pydantic_ai.models.google import GoogleModel, GoogleModelSettings

    settings = GoogleModelSettings(google_thinking_config={'thinking_budget': 2048})
    model = GoogleModel('gemini-2.5-flash')
    agent = Agent(model, model_settings=settings)

  • Disable thinking (budget 0):
    settings = GoogleModelSettings(google_thinking_config={'thinking_budget': 0})

Agent-level model_settings are merged with model-level and run-time settings (run-time has highest precedence). [1][2][3]

Sources:
[1] Pydantic AI — Google model docs (shows google_thinking_config & thinking_budget examples).
[2] Pydantic AI — Agents / ModelSettings docs (explains model_settings merging & usage).
[3] Pydantic AI — Thinking page (other model-specific thinking examples).


Incorrect parameter format for thinking_budget in model_settings.

For pydantic-ai 1.9.1 with Google Gemini models, thinking_budget must be passed via GoogleModelSettings(google_thinking_config={'thinking_budget': ...}), not as a flat dictionary key in model_settings.

The current code at line 34:

model_settings={"temperature": 0.3, "thinking_budget": 0}

Should be corrected to use the model-specific settings structure. This issue affects multiple agents in core/agents/: validate_blog_post_agent.py, title_suggestions_agent.py, summarize_page_agent.py, populate_competitor_details_agent.py, generate_blog_post_content_agent.py, extract_competitors_data_agent.py, extract_links_agent.py, insert_internal_links_agent.py, and analyze_project_agent.py.

🤖 Prompt for AI Agents
In core/agents/content_editor_agent.py around line 34, model_settings uses an
incorrect flat key for thinking_budget; for pydantic-ai 1.9.1 with Google Gemini
you must supply thinking_budget via GoogleModelSettings using the
google_thinking_config dict. Replace the current model_settings={"temperature":
0.3, "thinking_budget": 0} with a model-specific settings object that sets
temperature and uses
GoogleModelSettings(google_thinking_config={"thinking_budget": 0}) (or
equivalent in your codebase) so thinking_budget is passed under
google_thinking_config; apply the same pattern to the other listed agent files.

IMPORTANT: Be mindful that competitors are likely to speak in
{project.language} language.
"""
return f"Be mindful that competitors are likely to speak in {project.language} language." # noqa: E501
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directives.

The static analysis tool correctly identified that the # noqa: E501 comments are unnecessary since the E501 rule (line too long) is not enabled in your linter configuration.

Apply this diff to remove the unused directives:

-        return f"Be mindful that competitors are likely to speak in {project.language} language."  # noqa: E501
+        return f"Be mindful that competitors are likely to speak in {project.language} language."
-            return f"Only return competitors whose target audience is in {project.location}."  # noqa: E501
+            return f"Only return competitors whose target audience is in {project.location}."

Also applies to: 79-79

🧰 Tools
🪛 Ruff (0.14.4)

73-73: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In core/agents/find_competitors_agent.py lines 73 and 79, remove the unnecessary
"# noqa: E501" comments trailing the return statements; simply delete the "#
noqa: E501" tokens so the lines read normally without the unused noqa
directives, ensuring no other changes are made.

## Output Requirements:
Return the complete blog post content with internal links inserted. The content should be in Markdown format with proper link syntax.
""" # noqa: E501
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directive.

The static analysis tool correctly identified that the # noqa: E501 comment is unnecessary since the E501 rule is not enabled in your linter configuration.

Apply this diff:

 ## Output Requirements:
 Return the complete blog post content with internal links inserted. The content should be in Markdown format with proper link syntax.
-"""  # noqa: E501
+"""
📝 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
""" # noqa: E501
## Output Requirements:
Return the complete blog post content with internal links inserted. The content should be in Markdown format with proper link syntax.
"""
🧰 Tools
🪛 Ruff (0.14.4)

51-51: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In core/agents/insert_internal_links_agent.py around line 51, the trailing "#
noqa: E501" on the docstring is unnecessary; remove the unused noqa directive by
deleting " # noqa: E501" from the line (leave the docstring content intact) and
run linting to confirm no other violations remain.

Comment on lines +1 to +14
from django.db import models


class AIModel(models.TextChoices):
GEMINI_FLASH = "google-gla:gemini-2.5-flash", "Gemini 2.5 Flash"
PERPLEXITY_SONAR = "sonar", "Perplexity Sonar"


DEFAULT_AI_MODEL = AIModel.GEMINI_FLASH


def get_default_ai_model() -> str:
"""Returns the default AI model to use across the application."""
return DEFAULT_AI_MODEL
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

LGTM: Clean centralization of AI model configuration.

The new module provides a clean, centralized location for AI model definitions. The structure follows Django conventions with TextChoices and provides a clear default model selection mechanism.

However, please verify the model identifier "google-gla:gemini-2.5-flash" is correct. The "google-gla" prefix appears unusual for Gemini models.


🌐 Web query:

What is the correct model identifier for Google Gemini 2.5 Flash in pydantic-ai?

💡 Result:

The correct model identifier is "gemini-2.5-flash". [1][2]

Sources:
[1] Pydantic AI — Google models list (LatestGoogleModelNames includes "gemini-2.5-flash").
[2] Google Gemini models documentation (model code "gemini-2.5-flash").


Fix incorrect model identifier for Google Gemini 2.5 Flash.

The correct model identifier for Google Gemini 2.5 Flash in pydantic-ai is "gemini-2.5-flash", not "google-gla:gemini-2.5-flash". Update line 5 in core/agents/models.py:

GEMINI_FLASH = "gemini-2.5-flash", "Gemini 2.5 Flash"

The incorrect prefix will cause runtime failures when pydantic-ai attempts to load the model.

🤖 Prompt for AI Agents
In core/agents/models.py lines 1-14, the AIModel.GEMINI_FLASH enum value is
incorrect and will fail at runtime; change the enum member's stored string to
"gemini-2.5-flash" (i.e., GEMINI_FLASH = "gemini-2.5-flash", "Gemini 2.5 Flash")
and also ensure the default/exported value matches the hinted str type by
setting DEFAULT_AI_MODEL to AIModel.GEMINI_FLASH.value or by returning
DEFAULT_AI_MODEL.value from get_default_ai_model().

- Use '##' (h2 headers) for sections of the post where necessary.
- Don't use 3rd levle subheaders (###) or deeper. That should not be necessary for the post.
- Don't use 3rd level subheaders (###) or deeper. That should not be necessary for the post.
""" # noqa: E501
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directive.

The static analysis tool correctly identified that the # noqa: E501 comment is unnecessary since the E501 rule is not enabled.

Apply this diff:

         - Use '##' (h2 headers) for sections of the post where necessary.
         - Don't use 3rd level subheaders (###) or deeper. That should not be necessary for the post.
-    """  # noqa: E501
+    """
📝 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
""" # noqa: E501
"""
🧰 Tools
🪛 Ruff (0.14.4)

20-20: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In core/agents/system_prompts.py around line 20, remove the unnecessary "# noqa:
E501" directive at the end of the triple-quoted string closing line; simply
delete the comment so the line ends with the closing triple quotes, and ensure
no other unused noqa comments remain in the file.

Analyze the provided blog post content and determine if it meets publication quality standards.
Be thorough but fair. Minor imperfections are okay if the content is publishable.
""", # noqa: E501
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directive.

The static analysis tool correctly identified that the # noqa: E501 comment is unnecessary since the E501 rule is not enabled.

Apply this diff:

 Be thorough but fair. Minor imperfections are okay if the content is publishable.
-        """,  # noqa: E501
+        """,
📝 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
""", # noqa: E501
""",
🧰 Tools
🪛 Ruff (0.14.4)

22-22: Unused noqa directive (non-enabled: E501)

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In core/agents/validate_blog_post_agent.py around line 22, the trailing "# noqa:
E501" on the triple-quoted string is unnecessary (E501 not enabled); remove the
"# noqa: E501" directive so the line ends with the string delimiter only, and
run linters to confirm no other noqa directives are required.

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