Skip to content

Model Config: Add model configuration table and API endpoints#669

Closed
vprashrex wants to merge 1 commit intomainfrom
feature/model-config
Closed

Model Config: Add model configuration table and API endpoints#669
vprashrex wants to merge 1 commit intomainfrom
feature/model-config

Conversation

@vprashrex
Copy link
Collaborator

@vprashrex vprashrex commented Mar 12, 2026

Summary

Target issue is #635
Explain the motivation for making this change. What existing problem does the pull request solve?

Checklist

Before submitting a pull request, please ensure that you mark these task.

  • Ran fastapi run --reload app/main.py or docker compose up in the repository root and test.
  • If you've fixed a bug or added code that is tested and has test cases.

Notes

Please add here if any other information is required for the reviewer.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added model configuration API endpoints to browse available AI models and retrieve detailed information including configuration parameters, supported input/output modalities, and availability status.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

This PR introduces a complete model configuration management system with database schema, CRUD operations, and API endpoints for retrieving active AI model configurations with filtering and pagination support.

Changes

Cohort / File(s) Summary
Database Schema & Migration
backend/app/alembic/versions/050_create_model_config_table.py
Creates global.model_config table with comprehensive schema including provider, model_name, modality support, and activity flags. Seeds table with predefined OpenAI model configurations and provides downgrade path.
Domain Models
backend/app/models/model_config.py, backend/app/models/__init__.py
Defines SQLModel classes (ModelConfig, ModelConfigBase, ModelConfigPublic, ModelConfigListPublic) with unique constraint on (provider, model_name) and configurable fields for modalities, defaults, and activation status. Exports models via init.py.
Data Access Layer
backend/app/crud/model_config.py
Implements three query functions: get_active_models with optional provider filtering and pagination, get_model_config for single model lookup, and get_default_model_for_type for type-based model retrieval.
API Routes & Registration
backend/app/api/routes/model_config.py, backend/app/api/main.py
Adds FastAPI router with two endpoints: GET /models/ for listing active models with filters, and GET /models/{provider}/{model_name} for retrieving single model. Registers router in main API application.
API Documentation
backend/app/api/docs/model_config/list_models.md, backend/app/api/docs/model_config/get_model.md
Documents endpoint behavior, parameters, response schemas with example JSON responses showing model configuration structure including metadata, parameters, modalities, and timestamps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A new model home takes shape today,
Configurations neatly stored away,
With modalities dancing, providers aligned,
Lists and details perfectly designed,
Through CRUD and schema, the system finds its way! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changeset: addition of a model configuration table and corresponding API endpoints.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/model-config

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.

@vprashrex vprashrex self-assigned this Mar 12, 2026
@vprashrex vprashrex added the enhancement New feature or request label Mar 12, 2026
@vprashrex vprashrex linked an issue Mar 12, 2026 that may be closed by this pull request
@vprashrex vprashrex moved this to In Progress in Kaapi-dev Mar 12, 2026
@codecov
Copy link

codecov bot commented Mar 12, 2026

Codecov Report

❌ Patch coverage is 74.24242% with 17 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
backend/app/crud/model_config.py 44.44% 10 Missing ⚠️
backend/app/api/routes/model_config.py 63.15% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

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

🧹 Nitpick comments (3)
backend/app/crud/model_config.py (1)

14-20: Use SQLAlchemy boolean predicates instead of == True.

These filters work, but ModelConfig.is_active == True is the noisy/non-idiomatic form here and is already tripping Ruff. Prefer ModelConfig.is_active.is_(True) in all three queries.

♻️ Proposed fix
     statement = (
         select(ModelConfig)
         .where(
-            ModelConfig.is_active == True,
+            ModelConfig.is_active.is_(True),
             ModelConfig.default_for == completion_type,
         )
         .limit(1)
     )

-    statement = select(ModelConfig).where(ModelConfig.is_active == True)
+    statement = select(ModelConfig).where(ModelConfig.is_active.is_(True))

     statement = select(ModelConfig).where(
         ModelConfig.provider == provider,
         ModelConfig.model_name == model_name,
-        ModelConfig.is_active == True,
+        ModelConfig.is_active.is_(True),
     )

Also applies to: 32-32, 45-49

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/crud/model_config.py` around lines 14 - 20, Replace non-idiomatic
boolean comparisons using "== True" with SQLAlchemy boolean predicates: change
occurrences like ModelConfig.is_active == True to
ModelConfig.is_active.is_(True). Update this in all three query constructions
that reference ModelConfig.is_active (the select where block filtering by
ModelConfig.is_active and ModelConfig.default_for == completion_type and the
other two similar queries), ensuring you use .is_(True) for each boolean filter
while leaving other predicates (e.g., ModelConfig.default_for ==
completion_type) unchanged.
backend/app/api/routes/model_config.py (2)

19-25: Confirm the auth gate here is intentional.

Both handlers depend on AuthContextDep, and backend/app/api/deps.py:38-91 makes that a hard 401/403 gate. If the model catalog needs to be available before login or project selection, this will block it. If auth is intentional, rename the parameter to _auth_context (or suppress ARG001) so the dependency-only use is explicit.

Also applies to: 39-41

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/api/routes/model_config.py` around lines 19 - 25, The handler
list_models (and the other handler at lines 39-41) currently take AuthContextDep
as a required parameter which enforces the hard 401/403 gate from
backend/app/api/deps.py; decide whether the model catalog should be public or
protected and update accordingly: if it must be public, remove or make the auth
dependency optional (so the handlers don't require AuthContextDep), otherwise
keep the dependency but rename the parameter to _auth_context (or add an ARG001
suppression) to make the dependency-only usage explicit; reference the
AuthContextDep type and the list_models function (and the second handler) when
making the change.

26-30: Make count unambiguous for paginated responses.

After applying skip/limit, count=len(models) only reports the current page size. If clients use this field as pagination metadata, it will be misleading; either return the total matching rows or rename/drop the field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/api/routes/model_config.py` around lines 26 - 30, The code
returns count=len(models) after applying skip/limit (in the route using
get_active_models, APIResponse.success_response, and ModelConfigListPublic),
which makes pagination metadata ambiguous; change the endpoint to return the
total number of matching rows (not just the page length) by adding a separate
count query (e.g., implement/get a get_active_models_count or modify
get_active_models to return (items, total)), then pass that total into
ModelConfigListPublic as count (or rename/drop the field if you prefer
API-breaking behavior); update the route to use the new total value instead of
len(models) so clients receive unambiguous pagination info.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/app/alembic/versions/050_create_model_config_table.py`:
- Line 20: Add explicit return type hints to the Alembic entrypoint functions:
update the upgrade() and downgrade() function definitions to include return
annotations (e.g., def upgrade() -> None: and def downgrade() -> None:) so both
functions have explicit return types per the project typing guidelines; ensure
the annotations appear on the existing upgrade and downgrade functions in the
migration module.

In `@backend/app/models/model_config.py`:
- Around line 11-16: The model schema uses Python-side Literals (e.g., the
provider Field in model_config.py and the other Literal-backed fields around
lines 53-65 and 78-83) but does not enforce those allowed values at the DB
level; add SQL CHECK constraints to the SQLAlchemy model (e.g., on the provider
column and the default_for/other Literal-backed columns) to restrict values to
the same allowed set and update the Alembic migration to create the matching
CHECK constraints so values inserted outside the application cannot violate the
contract; ensure the constraint names are unique and descriptive and that the
model's sa_column includes sa.CheckConstraint(...) matching the Literal choices.

---

Nitpick comments:
In `@backend/app/api/routes/model_config.py`:
- Around line 19-25: The handler list_models (and the other handler at lines
39-41) currently take AuthContextDep as a required parameter which enforces the
hard 401/403 gate from backend/app/api/deps.py; decide whether the model catalog
should be public or protected and update accordingly: if it must be public,
remove or make the auth dependency optional (so the handlers don't require
AuthContextDep), otherwise keep the dependency but rename the parameter to
_auth_context (or add an ARG001 suppression) to make the dependency-only usage
explicit; reference the AuthContextDep type and the list_models function (and
the second handler) when making the change.
- Around line 26-30: The code returns count=len(models) after applying
skip/limit (in the route using get_active_models, APIResponse.success_response,
and ModelConfigListPublic), which makes pagination metadata ambiguous; change
the endpoint to return the total number of matching rows (not just the page
length) by adding a separate count query (e.g., implement/get a
get_active_models_count or modify get_active_models to return (items, total)),
then pass that total into ModelConfigListPublic as count (or rename/drop the
field if you prefer API-breaking behavior); update the route to use the new
total value instead of len(models) so clients receive unambiguous pagination
info.

In `@backend/app/crud/model_config.py`:
- Around line 14-20: Replace non-idiomatic boolean comparisons using "== True"
with SQLAlchemy boolean predicates: change occurrences like
ModelConfig.is_active == True to ModelConfig.is_active.is_(True). Update this in
all three query constructions that reference ModelConfig.is_active (the select
where block filtering by ModelConfig.is_active and ModelConfig.default_for ==
completion_type and the other two similar queries), ensuring you use .is_(True)
for each boolean filter while leaving other predicates (e.g.,
ModelConfig.default_for == completion_type) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44029a83-9a0c-41d5-8d67-c62c7e008119

📥 Commits

Reviewing files that changed from the base of the PR and between 48b0a6b and df0e840.

📒 Files selected for processing (8)
  • backend/app/alembic/versions/050_create_model_config_table.py
  • backend/app/api/docs/model_config/get_model.md
  • backend/app/api/docs/model_config/list_models.md
  • backend/app/api/main.py
  • backend/app/api/routes/model_config.py
  • backend/app/crud/model_config.py
  • backend/app/models/__init__.py
  • backend/app/models/model_config.py

depends_on = None


def upgrade():
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add return annotations to the Alembic entrypoints.

upgrade and downgrade are new Python functions and currently miss return type hints.

✏️ Proposed fix
-def upgrade():
+def upgrade() -> None:
     op.create_table(
         "model_config",
         ...
     )

-def downgrade():
+def downgrade() -> None:
     op.drop_table("model_config", schema="global")
As per coding guidelines, `**/*.py`: Always add type hints to all function parameters and return values in Python code.

Also applies to: 131-131

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/alembic/versions/050_create_model_config_table.py` at line 20,
Add explicit return type hints to the Alembic entrypoint functions: update the
upgrade() and downgrade() function definitions to include return annotations
(e.g., def upgrade() -> None: and def downgrade() -> None:) so both functions
have explicit return types per the project typing guidelines; ensure the
annotations appear on the existing upgrade and downgrade functions in the
migration module.

Comment on lines +11 to +16
provider: Literal["openai", "google"] = Field(
default="openai",
sa_column=sa.Column(
sa.String, nullable=False, comment="provider name (e.g. openai, google)"
),
)
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

Encode the allowed domains in the schema, not just the type hints.

Literal only constrains Python-side validation; the backing columns are still plain VARCHAR. A row inserted outside the API can persist unsupported provider or default_for values and leave the API serving data its own model contract says is impossible. Add CheckConstraints here and mirror them in the migration.

🛡️ Proposed fix
 class ModelConfig(ModelConfigBase, table=True):
     __tablename__ = "model_config"
     __table_args__ = (
+        sa.CheckConstraint(
+            "provider IN ('openai', 'google')",
+            name="ck_model_config_provider",
+        ),
+        sa.CheckConstraint(
+            "default_for IS NULL OR default_for IN ('text', 'stt', 'tts')",
+            name="ck_model_config_default_for",
+        ),
         sa.UniqueConstraint("provider", "model_name"),
         {"schema": "global"},
     )

Also applies to: 53-65, 78-83

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/models/model_config.py` around lines 11 - 16, The model schema
uses Python-side Literals (e.g., the provider Field in model_config.py and the
other Literal-backed fields around lines 53-65 and 78-83) but does not enforce
those allowed values at the DB level; add SQL CHECK constraints to the
SQLAlchemy model (e.g., on the provider column and the default_for/other
Literal-backed columns) to restrict values to the same allowed set and update
the Alembic migration to create the matching CHECK constraints so values
inserted outside the application cannot violate the contract; ensure the
constraint names are unique and descriptive and that the model's sa_column
includes sa.CheckConstraint(...) matching the Literal choices.

@Prajna1999 Prajna1999 linked an issue Mar 13, 2026 that may be closed by this pull request
@Prajna1999 Prajna1999 removed this from Kaapi-dev Mar 13, 2026
@vprashrex vprashrex closed this Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Config Management: Models(OpenAI) TTS/STT: API for exposing provider/model and config

2 participants