Skip to content

refactor(models): Refine MessageAgentThought SQLAlchemy typing#37

Open
tomerqodo wants to merge 2 commits intocoderabbit_full_base_refactormodels_refine_messageagentthought_sqlalchemy_typing_pr11from
coderabbit_full_head_refactormodels_refine_messageagentthought_sqlalchemy_typing_pr11
Open

refactor(models): Refine MessageAgentThought SQLAlchemy typing#37
tomerqodo wants to merge 2 commits intocoderabbit_full_base_refactormodels_refine_messageagentthought_sqlalchemy_typing_pr11from
coderabbit_full_head_refactormodels_refine_messageagentthought_sqlalchemy_typing_pr11

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 29, 2026

Benchmark PR from agentic-review-benchmarks#11

Summary by CodeRabbit

Release Notes

  • New Features

    • Added validation layer for agent thought data to ensure consistency and quality.
  • Improvements

    • Standardized numeric handling for pricing calculations using decimal types for precision.
    • Enhanced tool interaction tracking with robust parsing and data organization.
    • Restructured agent message schema for improved data organization and maintainability.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Walkthrough

Refactors agent thought processing with new Pydantic validation, switches to Decimal-based numeric handling, restructures the MessageAgentThought model with creator/chain tracking fields, and improves tool data parsing robustness in the organize_agent_history flow.

Changes

Cohort / File(s) Summary
Agent Thought Validation & Processing
api/core/agent/base_agent_runner.py
Introduces AgentThoughtValidation Pydantic model; converts pricing fields (message_unit_price, message_price_unit, answer_unit_price, answer_price_unit) to Decimal; sets created_by_role to CreatorUserRole.ACCOUNT enum; adds tool_process_data field initialization; refactors organize_agent_history to parse tool_input/observation safely and iterate over tool_names instead of raw tools string.
MessageAgentThought Model Schema
api/models/model.py
Changes base class from Base to TypeBase; restructures id column with insert_default and default_factory; adds created_by_role, created_by, and message_chain_id fields; updates thought, tool, tool_input, observation to Mapped[str | None]; converts pricing fields to Decimal types; adjusts created_at to init=False with server_default.
Test Fixture Updates
api/tests/test_containers_integration_tests/services/test_agent_service.py
Removes explicit id=fake.uuid4() initialization from MessageAgentThought instances in test helpers, allowing automatic ID generation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Thoughts now validated, prices shine in decimal gold,
Tools parse safer, chains are told,
The models dance with TypeBase grace,
IDs bloom without a trace! 🌿✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal and consists only of a reference to the source benchmark PR without providing substantive context about the changes, motivation, or impact. The description template requires a Summary section explaining the change and relevant context, which is missing. Add a proper Summary section explaining the purpose of the refactoring, what changes were made to MessageAgentThought and why they improve type safety and code quality.
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: refactoring the MessageAgentThought model to improve SQLAlchemy typing. The title is specific, concise, and directly reflects the primary focus of the changes across the modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
api/core/agent/base_agent_runner.py (1)

500-506: ToolPromptMessage content uses tool_inputs instead of tool_responses.

The ToolPromptMessage represents the tool's response/output, but line 502 retrieves content from tool_inputs instead of tool_responses. This appears semantically incorrect - the tool call response should contain the observation/output data, not the input data.

🐛 Proposed fix
                             tool_call_response.append(
                                 ToolPromptMessage(
-                                    content=str(tool_inputs.get(tool, agent_thought.observation)),
+                                    content=str(tool_responses.get(tool, agent_thought.observation)),
                                     name=tool,
                                     tool_call_id=tool_call_id,
                                 )
                             )
🤖 Fix all issues with AI agents
In `@api/core/agent/base_agent_runner.py`:
- Around line 319-326: The answer_unit_price and answer_price_unit are swapped
compared to the message_* pattern and model defaults; in base_agent_runner where
the row is constructed (fields message_unit_price, message_price_unit,
answer_unit_price, answer_price_unit), change the answer_* values to mirror the
message_* order so answer_unit_price=Decimal(0) and
answer_price_unit=Decimal("0.001") (matching the model.py defaults for
*_price_unit and the existing message_* assignments).

In `@api/models/model.py`:
- Around line 1856-1857: The mapped columns tool_labels_str and tool_meta_str
currently use default=sa.text("'{}'") which is incorrect; change them to use a
Python-side default default="{}" and, if a DB-level default is desired, add
server_default=sa.text("'{}'") instead of using sa.text() in the default
parameter so object instantiation gets the string "{}" and the database still
has the same fallback.
🧹 Nitpick comments (3)
api/models/model.py (1)

1864-1876: Inconsistent Numeric precision specification for price fields.

message_unit_price and answer_unit_price (lines 1864, 1871) use sa.Numeric without precision, while message_price_unit and answer_price_unit (lines 1865-1866, 1872-1873) specify sa.Numeric(10, 7). This inconsistency could lead to different storage behavior across databases and potential precision loss.

♻️ Proposed fix to standardize precision
-    message_unit_price: Mapped[Decimal | None] = mapped_column(sa.Numeric, nullable=True, default=None)
+    message_unit_price: Mapped[Decimal | None] = mapped_column(sa.Numeric(10, 7), nullable=True, default=None)
     message_price_unit: Mapped[Decimal] = mapped_column(
         sa.Numeric(10, 7), nullable=False, default=Decimal("0.001"), server_default=sa.text("0.001")
     )
     message_files: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)
     answer: Mapped[str | None] = mapped_column(LongText, nullable=True, default=None)
     answer_token: Mapped[int | None] = mapped_column(sa.Integer, nullable=True, default=None)
-    answer_unit_price: Mapped[Decimal | None] = mapped_column(sa.Numeric, nullable=True, default=None)
+    answer_unit_price: Mapped[Decimal | None] = mapped_column(sa.Numeric(10, 7), nullable=True, default=None)
api/core/agent/base_agent_runner.py (2)

52-66: Unused Pydantic model and inconsistent comment.

AgentThoughtValidation is defined but never instantiated or used anywhere in this file. Additionally, line 65's comment suggests using extra='forbid' but the actual value is "allow", which is contradictory.

Either:

  1. Remove this class if it's not needed
  2. Use it for validation before creating MessageAgentThought instances
  3. Fix the comment to match the intended behavior

328-328: Use Decimal for total_price for type consistency.

Other price-related fields use Decimal, but total_price=0 uses an integer literal. For consistency with the model's Mapped[Decimal | None] type, this should be Decimal(0).

♻️ Proposed fix
-            total_price=0,
+            total_price=Decimal(0),
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55eeb1d and 5209691.

📒 Files selected for processing (3)
  • api/core/agent/base_agent_runner.py
  • api/models/model.py
  • api/tests/test_containers_integration_tests/services/test_agent_service.py
💤 Files with no reviewable changes (1)
  • api/tests/test_containers_integration_tests/services/test_agent_service.py
🧰 Additional context used
🧬 Code graph analysis (2)
api/core/agent/base_agent_runner.py (1)
api/models/enums.py (1)
  • CreatorUserRole (6-8)
api/models/model.py (2)
api/models/base.py (1)
  • TypeBase (17-22)
api/models/types.py (2)
  • StringUUID (15-38)
  • LongText (41-61)
🔇 Additional comments (3)
api/models/model.py (1)

1838-1881: LGTM on the overall model refactoring.

The transition to TypeBase, addition of explicit Mapped type annotations, and the new creator/chain tracking fields (created_by_role, created_by, message_chain_id) align well with the SQLAlchemy 2.0 dataclass pattern shown in the TypeBase definition.

api/core/agent/base_agent_runner.py (2)

364-366: LGTM on thought concatenation refactor.

The updated logic properly handles the case where agent_thought.thought may be None by defaulting to an empty string before concatenation.


464-518: LGTM on the refactored tool parsing logic.

The improved parsing with explicit try/except blocks for JSON parsing and proper fallback handling (tool_inputs and tool_responses dictionaries) makes the code more robust against malformed data.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +319 to +326
message_unit_price=Decimal(0),
message_price_unit=Decimal("0.001"),
message_files=json.dumps(messages_ids) if messages_ids else "",
answer="",
observation="",
answer_token=0,
answer_unit_price=0,
answer_price_unit=0,
answer_unit_price=Decimal("0.001"),
answer_price_unit=Decimal(0),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

answer_unit_price and answer_price_unit values appear swapped.

The initialization pattern for message fields (lines 319-320) is:

  • message_unit_price=Decimal(0)
  • message_price_unit=Decimal("0.001")

But for answer fields (lines 325-326):

  • answer_unit_price=Decimal("0.001")
  • answer_price_unit=Decimal(0)

This is inconsistent. The model in model.py defines both *_price_unit fields with default=Decimal("0.001"), suggesting the values should be swapped.

🐛 Proposed fix
             answer="",
             observation="",
             answer_token=0,
-            answer_unit_price=Decimal("0.001"),
-            answer_price_unit=Decimal(0),
+            answer_unit_price=Decimal(0),
+            answer_price_unit=Decimal("0.001"),
📝 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
message_unit_price=Decimal(0),
message_price_unit=Decimal("0.001"),
message_files=json.dumps(messages_ids) if messages_ids else "",
answer="",
observation="",
answer_token=0,
answer_unit_price=0,
answer_price_unit=0,
answer_unit_price=Decimal("0.001"),
answer_price_unit=Decimal(0),
message_unit_price=Decimal(0),
message_price_unit=Decimal("0.001"),
message_files=json.dumps(messages_ids) if messages_ids else "",
answer="",
observation="",
answer_token=0,
answer_unit_price=Decimal(0),
answer_price_unit=Decimal("0.001"),
🤖 Prompt for AI Agents
In `@api/core/agent/base_agent_runner.py` around lines 319 - 326, The
answer_unit_price and answer_price_unit are swapped compared to the message_*
pattern and model defaults; in base_agent_runner where the row is constructed
(fields message_unit_price, message_price_unit, answer_unit_price,
answer_price_unit), change the answer_* values to mirror the message_* order so
answer_unit_price=Decimal(0) and answer_price_unit=Decimal("0.001") (matching
the model.py defaults for *_price_unit and the existing message_* assignments).

Comment on lines +1856 to +1857
tool_labels_str: Mapped[str] = mapped_column(LongText, nullable=False, default=sa.text("'{}'"))
tool_meta_str: Mapped[str] = mapped_column(LongText, nullable=False, default=sa.text("'{}'"))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Incorrect use of sa.text() with default parameter.

The default parameter in SQLAlchemy's mapped_column expects a Python value or callable, not an sa.text() expression. Using sa.text("'{}'") as default will not produce the intended "{}" string value during Python-side object instantiation - it will store the sa.text() object itself or cause type errors.

Use server_default=sa.text("'{}'") for database-level defaults and default="{}" for Python-side defaults.

🐛 Proposed fix
-    tool_labels_str: Mapped[str] = mapped_column(LongText, nullable=False, default=sa.text("'{}'"))
-    tool_meta_str: Mapped[str] = mapped_column(LongText, nullable=False, default=sa.text("'{}'"))
+    tool_labels_str: Mapped[str] = mapped_column(LongText, nullable=False, default="{}", server_default=sa.text("'{}'"))
+    tool_meta_str: Mapped[str] = mapped_column(LongText, nullable=False, default="{}", server_default=sa.text("'{}'"))
📝 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
tool_labels_str: Mapped[str] = mapped_column(LongText, nullable=False, default=sa.text("'{}'"))
tool_meta_str: Mapped[str] = mapped_column(LongText, nullable=False, default=sa.text("'{}'"))
tool_labels_str: Mapped[str] = mapped_column(LongText, nullable=False, default="{}", server_default=sa.text("'{}'"))
tool_meta_str: Mapped[str] = mapped_column(LongText, nullable=False, default="{}", server_default=sa.text("'{}'"))
🤖 Prompt for AI Agents
In `@api/models/model.py` around lines 1856 - 1857, The mapped columns
tool_labels_str and tool_meta_str currently use default=sa.text("'{}'") which is
incorrect; change them to use a Python-side default default="{}" and, if a
DB-level default is desired, add server_default=sa.text("'{}'") instead of using
sa.text() in the default parameter so object instantiation gets the string "{}"
and the database still has the same fallback.

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.

1 participant