-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: model history table with more fields #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is being reviewed by Cursor Bugbot
Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
crates/database/src/migrations/sql/V0028__refactor_model_pricing_history_to_model_history.sql
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors the model pricing history table into a comprehensive model history table that captures complete snapshots of model state changes with full audit tracking capabilities.
Key Changes:
- Renamed
model_pricing_historytomodel_historyand expanded it to include all model fields (name, icon, verifiable, is_active) for complete state snapshots - Replaced generic
changed_bystring field with structured user audit fields (changed_by_user_id,changed_by_user_email) - Updated model upsert and soft delete operations to automatically record history with change reasons and user context
- Added
changeReasonparameter to admin model update/delete APIs andDeleteModelRequesttype for optional delete reasons
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
crates/database/src/migrations/sql/V0023__refactor_model_pricing_history_to_model_history.sql |
Migration to rename table, add new columns (model_name, model_icon, verifiable, is_active), backfill data, rename indexes, drop old trigger/function, and add user audit columns |
crates/database/src/models.rs |
Renamed ModelPricingHistory to ModelHistory and added new fields (model_name, model_icon, verifiable, is_active, changed_by_user_id, changed_by_user_email) plus audit fields to UpdateModelPricingRequest |
crates/database/src/repositories/model.rs |
Added record_model_history helper method to close previous history and insert new snapshots; updated upsert_model_pricing and soft_delete_model to record history; renamed query methods; added default soft delete reason constant |
crates/database/src/repositories/admin_composite.rs |
Updated to pass through new audit fields (change_reason, changed_by_user_id, changed_by_user_email) and map expanded history fields in responses |
crates/services/src/admin/ports.rs |
Added audit fields to UpdateModelAdminRequest, expanded ModelHistoryEntry with new fields, and updated soft_delete_model and delete_model signatures to accept audit parameters |
crates/services/src/admin/mod.rs |
Updated delete_model implementation to accept and pass through change_reason and user audit parameters |
crates/api/src/routes/admin.rs |
Extract admin user context in batch_upsert_models and delete_model; pass user ID/email for audit tracking; updated get_model_history response mapping; added DeleteModelRequest body parameter |
crates/api/src/models.rs |
Added changeReason field to UpdateModelApiRequest, created DeleteModelRequest type, and expanded ModelHistoryEntry with new fields matching database schema |
crates/api/tests/model_history_test.rs |
Comprehensive E2E tests covering history creation, updates with proper ordering, soft delete with/without custom reasons, user tracking, and progression through multiple updates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
9b0060c to
704c8c5
Compare
704c8c5 to
038ad4d
Compare
7a67c8a to
c74cccb
Compare
27323f9 to
cdc1107
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 13 out of 15 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.
| input_cost_per_token = COALESCE(EXCLUDED.input_cost_per_token, models.input_cost_per_token), | ||
| output_cost_per_token = COALESCE(EXCLUDED.output_cost_per_token, models.output_cost_per_token), | ||
| model_display_name = COALESCE(EXCLUDED.model_display_name, models.model_display_name), | ||
| model_description = COALESCE(EXCLUDED.model_description, models.model_description), | ||
| model_icon = COALESCE(EXCLUDED.model_icon, models.model_icon), | ||
| context_length = COALESCE(EXCLUDED.context_length, models.context_length), | ||
| verifiable = COALESCE(EXCLUDED.verifiable, models.verifiable), | ||
| is_active = COALESCE(EXCLUDED.is_active, models.is_active), |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using COALESCE(EXCLUDED.*, models.*) in the ON CONFLICT UPDATE clause means that NULL values in the insert will preserve existing values. However, this conflicts with the partial update pattern used in the UPDATE branch (lines 226-235) where NULL means 'no change'. For consistency and to avoid confusion, the INSERT path should use the same semantics. Consider using separate code paths or explicitly handling which fields can be NULL vs which represent 'no update'.
| input_cost_per_token = COALESCE(EXCLUDED.input_cost_per_token, models.input_cost_per_token), | |
| output_cost_per_token = COALESCE(EXCLUDED.output_cost_per_token, models.output_cost_per_token), | |
| model_display_name = COALESCE(EXCLUDED.model_display_name, models.model_display_name), | |
| model_description = COALESCE(EXCLUDED.model_description, models.model_description), | |
| model_icon = COALESCE(EXCLUDED.model_icon, models.model_icon), | |
| context_length = COALESCE(EXCLUDED.context_length, models.context_length), | |
| verifiable = COALESCE(EXCLUDED.verifiable, models.verifiable), | |
| is_active = COALESCE(EXCLUDED.is_active, models.is_active), | |
| input_cost_per_token = CASE WHEN EXCLUDED.input_cost_per_token IS NOT NULL THEN EXCLUDED.input_cost_per_token ELSE models.input_cost_per_token END, | |
| output_cost_per_token = CASE WHEN EXCLUDED.output_cost_per_token IS NOT NULL THEN EXCLUDED.output_cost_per_token ELSE models.output_cost_per_token END, | |
| model_display_name = CASE WHEN EXCLUDED.model_display_name IS NOT NULL THEN EXCLUDED.model_display_name ELSE models.model_display_name END, | |
| model_description = CASE WHEN EXCLUDED.model_description IS NOT NULL THEN EXCLUDED.model_description ELSE models.model_description END, | |
| model_icon = CASE WHEN EXCLUDED.model_icon IS NOT NULL THEN EXCLUDED.model_icon ELSE models.model_icon END, | |
| context_length = CASE WHEN EXCLUDED.context_length IS NOT NULL THEN EXCLUDED.context_length ELSE models.context_length END, | |
| verifiable = CASE WHEN EXCLUDED.verifiable IS NOT NULL THEN EXCLUDED.verifiable ELSE models.verifiable END, | |
| is_active = CASE WHEN EXCLUDED.is_active IS NOT NULL THEN EXCLUDED.is_active ELSE models.is_active END, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed via a shared table lock
crates/database/src/migrations/sql/V0028__refactor_model_pricing_history_to_model_history.sql
Show resolved
Hide resolved
Makefile
Outdated
| @echo "Formatting code with rustfmt..." | ||
| cargo fmt | ||
|
|
||
| check-clippy: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check-clippy is identical to lint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
| @@ -0,0 +1,100 @@ | |||
| use std::fs; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move the seed related code and SQLs to tests folder as it's only needed for testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's also used by local development as well, when you try to create a new model
c1d7138 to
0783225
Compare
| @@ -0,0 +1,94 @@ | |||
| .PHONY: help seed api dev build test test-unit test-integration lint fmt check-fmt preflight clean | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use https://github.com/sagiegurari/cargo-make ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haven't used that one before, if it provides more benefits than makefile we can definitely switch to it
Note
Refactors model history to a richer table and repository with audit tracking, updates admin APIs to record/expose changes (including soft delete reason), and adds seed tooling, tests, and Makefile/README updates.
model_pricing_history→model_history; addmodel_name,model_icon,verifiable,is_active,changed_by_user_id,changed_by_user_email; drop trigger andchanged_by.changeReason; new history getters and mappings.seedbinary to run migrations and SQL seeds; add mock admin seed (001__seed_mock_admin.sql); addtracing-subscriberdep.UpdateModelApiRequestwithchangeReason; addDeleteModelRequest(optional reason).changed_by_user_id/emailandchangeReasonon upsert/delete; delete accepts optional body; history response includesmodelName,modelIcon,verifiable,isActive,changedByUserId/Email.Makefile(dev/test/lint/fmt/preflight) and README updates to usemakecommands.Written by Cursor Bugbot for commit e782ea5. This will update automatically on new commits. Configure here.