Skip to content

Improve API docs#1182

Merged
scosman merged 26 commits into
mainfrom
scosman/improve-api-docs
Apr 1, 2026
Merged

Improve API docs#1182
scosman merged 26 commits into
mainfrom
scosman/improve-api-docs

Conversation

@scosman

@scosman scosman commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Overhaul of the Kiln OpenAPI spec to make it usable for AI agents. Previously 87% of endpoints had no description, no parameters were documented, 65% of schemas were undescribed, paths were inconsistent (singular/plural mixed), and nothing was tagged.

Changes

Documentation-only (no behavior changes):

  • Added descriptions to ~150 operations via docstrings
  • Added Path(description=...) / Query(description=...) to all ~315 parameters
  • Added docstrings and Field(description=...) to ~172 Pydantic schemas and ~788 properties
  • Improved ambiguous summaries (e.g. "Edit Tags" → "Edit Run Tags" / "Edit Document Tags")
  • Cleaned up check_entitlements description
  • Added OpenAPI tags to all 173 endpoints across 16 tag groups (Projects, Tasks, Runs, Evals, etc.)

Breaking API changes (frontend updated in same PR):

  • Standardized paths to plural nouns: /task → /tasks, /spec → /specs, /eval → /evals
  • Unified run config paths: /task_run_config → /run_configs, /mcp_run_config → /run_configs/mcp
  • Renamed repair endpoints: /run_repair → /generate_repair, /repair → /save_repair
  • Renamed eval endpoints: /run_task_run_eval → /run_comparison, /run_eval_config_eval → /run_calibration
  • Changed provider connect endpoints from GET to POST
  • Regenerated api_schema.d.ts and updated all frontend path references

Summary by CodeRabbit

  • New Features

    • Richer OpenAPI metadata across APIs (operation summaries, tags, and parameter/schema descriptions) and added model field descriptions for clearer docs.
  • Bug Fixes

    • Provider connect endpoints changed to POST; client and test calls updated accordingly.
    • Various API path fixes to align pluralization (tasks, specs, evals, run_configs) and repair endpoint renames (generate_repair/save_repair).
  • Documentation

    • Added comprehensive API design, implementation plans, phase plans, and architecture/overview specs; frontend/tests updated to match API changes.

scosman and others added 7 commits March 20, 2026 15:50
Phase 2 of OpenAPI documentation improvements. Adds Path(description=...), Query(description=...), and Body(description=...) annotations to all parameters across both libs/server/ and app/desktop/studio_server/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Path(description=...) and Query(description=...) annotations to all
undescribed parameters across libs/server/ and app/desktop/studio_server/.
Covers 19 files including tool_api, skill_api, copilot_api,
prompt_optimization_job_api, run_config_api, and import_api which were
missed in the initial pass.
Added docstrings and Field(description=...) across core datamodels, server API models, and desktop server models to improve OpenAPI spec documentation.
Backend: singular→plural path standardization (tasks, prompts, specs, evals), run config path unification, repair endpoint renames, eval endpoint renames, GET→POST for provider connect endpoints.
Frontend: regenerated api_schema.d.ts, updated all path references in stores and components.
Added tags to endpoints in document_api, tool_api, skill_api, prompt_api, copilot_api, run_config_api, prompt_optimization_job_api, and import_api that were showing up in an untagged 'Other API' group.
@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds OpenAPI metadata (summaries, tags, parameter descriptions) and Pydantic Field descriptions across many backend APIs and datamodels; pluralizes several REST segments (e.g., /task→/tasks, /eval→/evals, /spec→/specs); switches two provider connect endpoints GET→POST; renames repair endpoints; updates matching frontend calls and tests.

Changes

Cohort / File(s) Summary
Desktop API: route metadata & param annotations
app/desktop/studio_server/copilot_api.py, app/desktop/studio_server/data_gen_api.py, app/desktop/studio_server/eval_api.py, app/desktop/studio_server/finetune_api.py, app/desktop/studio_server/repair_api.py, app/desktop/studio_server/run_config_api.py, app/desktop/studio_server/tool_api.py, app/desktop/studio_server/prompt_api.py, app/desktop/studio_server/prompt_optimization_job_api.py, app/desktop/studio_server/import_api.py, app/desktop/studio_server/settings_api.py
Added summary/tags to many FastAPI routes; converted path/query params to Annotated[..., Path(...)]/Annotated[..., Query(...)] with descriptions; pluralized selected route segments and renamed repair endpoints; changed two provider connect routes from GET→POST.
Desktop API: Pydantic Field metadata
app/desktop/studio_server/copilot_models.py, app/desktop/studio_server/skill_api.py, app/desktop/studio_server/finetune_api.py
Changed many model fields to use Field(..., description=...) and explicit defaults/default_factory where applicable; no runtime logic changes.
Desktop API tests
app/desktop/studio_server/test_eval_api.py, app/desktop/studio_server/test_prompt_api.py, app/desktop/studio_server/test_provider_api.py, app/desktop/studio_server/test_repair_api.py, app/desktop/studio_server/test_run_config_api.py, app/desktop/studio_server/test_server.py
Updated test request URLs and HTTP methods to match backend route renames and provider connect method changes.
Server API: route metadata, param annotations & Pydantic fields
libs/server/kiln_server/document_api.py, libs/server/kiln_server/prompt_api.py, libs/server/kiln_server/spec_api.py, libs/server/kiln_server/run_api.py, libs/server/kiln_server/task_api.py, libs/server/kiln_server/project_api.py, libs/server/kiln_server/server.py
Added summary/tags and Annotated param metadata to many handlers; pluralized routes (/task/tasks, /spec/specs, /eval/evals); added Field(...) descriptions to numerous request/response models.
Server API tests
libs/server/kiln_server/test_prompt_api.py, libs/server/kiln_server/test_spec_api.py, libs/server/kiln_server/test_task_api.py
Updated tests to use pluralized routes and adjusted paths/methods where changed.
Core datamodels: docstrings & Field metadata
libs/core/kiln_ai/datamodel/basemodel.py, libs/core/kiln_ai/datamodel/task.py, libs/core/kiln_ai/datamodel/extraction.py, libs/core/kiln_ai/datamodel/chunk.py, libs/core/kiln_ai/datamodel/embedding.py, libs/core/kiln_ai/datamodel/eval.py, libs/core/kiln_ai/datamodel/rag.py, libs/core/kiln_ai/datamodel/*
Added class/module docstrings and Field(description=...) annotations to many datamodel classes/fields; some id fields now use explicit default_factory.
Frontend: API path updates & provider connect changes
app/web_ui/src/lib/stores.ts, app/web_ui/src/lib/stores/*, many app/web_ui/src/routes/.../*.svelte, app/web_ui/src/routes/(fullscreen)/setup/.../connect_providers/connect_providers.svelte
Updated hardcoded API paths in stores/pages to match backend pluralization and renamed endpoints; changed client calls for provider connects from GET→POST and adjusted request payload locations.
Spec docs & plans
specs/projects/document_api/*.md, specs/projects/document_api/phase_plans/*
Added architecture, functional spec, implementation plan, and phase documents describing the OpenAPI/schema improvement rollout and verification steps.
Misc small UI fixes
app/web_ui/src/routes/(app)/docs/rag_configs/.../table_rag_config_row.svelte
Fixed null handling when passing reranker config to helper (?? null).

Sequence Diagram(s)

(Skipped — changes are documentation/schema metadata and route renames rather than new multi-component control flow.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

"🐰
I hopped through routes and fields with care,
Turned singulars plural and left notes to share.
Tags and descriptions now bloom on each line,
Frontend and server aligned, tidy and fine.
A nibble of joy — docs and code now pair."

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% 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 "Improve API docs" accurately reflects the main focus of this large PR, which is an overhaul of the Kiln OpenAPI specification and documentation across operations, parameters, schemas, and endpoint tagging.
Description check ✅ Passed The PR description comprehensively documents both documentation-only changes (descriptions, Field metadata, tags) and breaking API changes (path pluralization, endpoint renames, HTTP method changes), matching the template structure with a clear summary and detailed changes section.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch scosman/improve-api-docs

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.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request significantly enhances the OpenAPI documentation for the Kiln AI API by adding comprehensive descriptions, summaries, and tags to FastAPI endpoints and Pydantic models. It standardizes API paths to plural forms, unifies run configuration endpoints, and corrects HTTP method usage for mutations. Corresponding updates were made to the Svelte frontend to maintain compatibility with the revised API schema. Feedback includes a correction for a query parameter nesting issue in the frontend, a warning regarding mutable default arguments in Python, and a type correction for a path parameter.

run_config_ids: Annotated[
list[str],
Query(description="The list of run configuration IDs to evaluate."),
] = [],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using a mutable default value like [] for a function argument is a common pitfall in Python. While FastAPI's Query parameter handling often mitigates this, it's safer and more idiomatic to use None as the default and initialize it to an empty list within the function body if needed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not an issue here — FastAPI's Query() creates a new list per request, so the mutable default pitfall doesn't apply.

Comment thread app/desktop/studio_server/eval_api.py Outdated
@github-actions

github-actions Bot commented Mar 31, 2026

Copy link
Copy Markdown

📊 Coverage Report

Overall Coverage: 91%

Diff: origin/main...HEAD

  • app/desktop/studio_server/api_models/copilot_models.py (100%)
  • app/desktop/studio_server/copilot_api.py (100%)
  • app/desktop/studio_server/data_gen_api.py (100%)
  • app/desktop/studio_server/eval_api.py (100%)
  • app/desktop/studio_server/finetune_api.py (100%)
  • app/desktop/studio_server/import_api.py (100%)
  • app/desktop/studio_server/prompt_api.py (100%)
  • app/desktop/studio_server/prompt_optimization_job_api.py (100%)
  • app/desktop/studio_server/provider_api.py (100%)
  • app/desktop/studio_server/repair_api.py (100%)
  • app/desktop/studio_server/run_config_api.py (100%)
  • app/desktop/studio_server/settings_api.py (100%)
  • app/desktop/studio_server/skill_api.py (100%)
  • app/desktop/studio_server/tool_api.py (100%)
  • libs/core/kiln_ai/datamodel/basemodel.py (100%)
  • libs/core/kiln_ai/datamodel/datamodel_enums.py (100%)
  • libs/core/kiln_ai/datamodel/extraction.py (100%)
  • libs/core/kiln_ai/datamodel/spec.py (100%)
  • libs/core/kiln_ai/datamodel/task.py (100%)
  • libs/core/kiln_ai/datamodel/task_output.py (100%)
  • libs/core/kiln_ai/utils/validation.py (100%)
  • libs/server/kiln_server/document_api.py (100%)
  • libs/server/kiln_server/project_api.py (100%)
  • libs/server/kiln_server/prompt_api.py (100%)
  • libs/server/kiln_server/run_api.py (100%)
  • libs/server/kiln_server/server.py (100%)
  • libs/server/kiln_server/spec_api.py (100%)
  • libs/server/kiln_server/task_api.py (100%)

Summary

  • Total: 491 lines
  • Missing: 0 lines
  • Coverage: 100%

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

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

⚠️ Outside diff range comments (5)
app/desktop/studio_server/settings_api.py (1)

46-77: ⚠️ Potential issue | 🟠 Major

Preserve HTTPException semantics and avoid returning raw exception text.

Line 76 currently converts all exceptions to 500 and exposes detail=str(e). That masks intended HTTP status codes (like 404) and leaks internals.

Proposed fix
 def open_logs():
     """Opens the log folder in the system file browser."""
     try:
         open_logs_folder()
         return {"message": "opened"}
-    except Exception as e:
-        raise HTTPException(status_code=500, detail=str(e))
+    except Exception:
+        raise HTTPException(status_code=500, detail="Failed to open logs folder.")

 def open_project_folder(
@@
     """Opens the project folder in the system file browser."""
     try:
         project = project_from_id(project_id)
@@
         open_folder(path)
         return {"message": "opened"}
-    except Exception as e:
-        raise HTTPException(status_code=500, detail=str(e))
+    except HTTPException:
+        raise
+    except Exception:
+        raise HTTPException(status_code=500, detail="Failed to open project folder.")
Based on learnings: "The `project_from_id` function in `kiln_server.project_api` already handles HTTP errors internally, raising HTTPException with status code 404 when a project is not found, so additional error handling around calls to this function is not needed."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/settings_api.py` around lines 46 - 77, The
open_project_folder handler currently catches all exceptions and returns HTTP
500 with detail=str(e), which masks intended HTTPExceptions (e.g., from
project_from_id) and leaks internals; change the error handling in
open_project_folder so that it does not convert existing HTTPException
instances—either remove the broad try/except or re-raise if isinstance(e,
HTTPException)—and for unexpected errors raise a new
HTTPException(status_code=500, detail="Internal server error") (do not include
str(e)); keep the call to project_from_id and open_folder unchanged so
project_from_id can raise its own 404 as designed.
libs/server/kiln_server/project_api.py (2)

107-109: ⚠️ Potential issue | 🟠 Major

Guard config projects before filtering.

Line 108 can raise TypeError if Config.shared().projects is None or non-list (which is already handled in add_project_to_config). Make delete resilient to the same state.

🛠️ Suggested fix
-        projects_before = Config.shared().projects
+        projects_before = Config.shared().projects
+        if not isinstance(projects_before, list):
+            projects_before = []
         projects_after = [p for p in projects_before if p != str(project.path)]
         Config.shared().save_setting("projects", projects_after)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/server/kiln_server/project_api.py` around lines 107 - 109, Guard against
Config.shared().projects being None or non-list before filtering: retrieve
projects_before = Config.shared().projects, if not isinstance(projects_before,
list) then normalize it to an empty list (or to list(projects_before) if you
expect other iterables), compute projects_after by filtering that normalized
list (e.g., [p for p in normalized if p != str(project.path)]), and then call
Config.shared().save_setting("projects", projects_after); update the code around
projects_before/projects_after to use the normalized variable so deletion is
resilient to None or wrong types.

65-68: ⚠️ Potential issue | 🟠 Major

Use the validated model instance before saving.

On line 67, Project.model_validate(...) is called for validation but its result is ignored. Since model_copy(update=...) does not validate the updated fields, any coercion or normalization (e.g., the name field's FilenameString validator) is skipped. Line 68 then persists the unvalidated updated_project.

Suggested fix
-        updated_project = original_project.model_copy(update=project_updates)
-        # Force validation using model_validate()
-        Project.model_validate(updated_project.model_dump())
-        updated_project.save_to_file()
-        return updated_project
+        updated_project = original_project.model_copy(update=project_updates)
+        # Force validation and keep the validated/coerced model
+        validated_project = Project.model_validate(updated_project.model_dump())
+        validated_project.save_to_file()
+        return validated_project
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/server/kiln_server/project_api.py` around lines 65 - 68, The code calls
Project.model_validate(...) but ignores its return value, so
coercions/validators from Project aren't applied before saving; replace the
sequence that uses updated_project.model_copy(update=project_updates) followed
by a discarded Project.model_validate(...) by validating and using the validated
instance—call Project.model_validate(...) (or updated_project.model_validate()
if available) and assign its result to updated_project, then call
updated_project.save_to_file() so the validated/coerced model is what's
persisted; reference updated_project, Project.model_validate, model_copy, and
save_to_file when making the change.
libs/server/kiln_server/test_task_api.py (1)

96-104: ⚠️ Potential issue | 🔴 Critical

Bug: Mock is not active when making the request.

The response = client.post(...) call at line 101 is outside the with patch(...) context manager (which ends at line 99). This means mock_load.side_effect won't be applied when the request is made, so the test won't actually test the error handling path.

🐛 Proposed fix to move the request inside the context manager
     with patch("kiln_server.task_api.project_from_id") as mock_load:
         mock_load.side_effect = HTTPException(
             status_code=404, detail="Project not found"
         )
-
-    response = client.post("/api/projects/FAKEPROJECTID/tasks", json=task_data)
+        response = client.post("/api/projects/FAKEPROJECTID/tasks", json=task_data)
 
     assert response.status_code == 404
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/server/kiln_server/test_task_api.py` around lines 96 - 104, The test's
HTTPException side effect on project_from_id isn't applied because the
client.post call is outside the with patch(...) context; move the request into
the same with patch("kiln_server.task_api.project_from_id") as mock_load: block
so mock_load.side_effect = HTTPException(...) is active when calling
client.post("/api/projects/FAKEPROJECTID/tasks", json=task_data), then assert
response.status_code and response.json()["message"] as written.
app/desktop/studio_server/eval_api.py (1)

524-552: ⚠️ Potential issue | 🟠 Major

Validate train_set_filter_id before saving it.

This patch path still stores any string into eval.train_set_filter_id. Later flows in this file resolve that value through dataset_filter_from_id(), so a typo here becomes persisted invalid state and breaks follow-on eval APIs instead of failing fast on the PATCH request.

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

In `@app/desktop/studio_server/eval_api.py` around lines 524 - 552, The
update_eval handler currently assigns request.train_set_filter_id into
eval.train_set_filter_id without validating it; before setting it (inside
update_eval and after the existing "already set" guard), call the same resolver
used elsewhere (dataset_filter_from_id) with the project_id and
request.train_set_filter_id to ensure the filter exists/loads successfully, and
if that resolver fails or returns None raise an HTTPException 400 with a clear
message; only assign eval.train_set_filter_id after successful validation so
typos or invalid IDs fail fast on PATCH.
🧹 Nitpick comments (1)
app/desktop/studio_server/settings_api.py (1)

23-67: Add explicit return types on the touched handlers.

update_settings, read_setting_item, open_logs, and open_project_folder in this changed block should declare concrete return types for stronger typing contracts.

Proposed type annotations
 def connect_settings(app: FastAPI):
@@
-    def update_settings(
+    def update_settings(
         new_settings: dict[str, int | float | str | bool | list | None],
-    ):
+    ) -> dict[str, Any]:
@@
-    def read_setting_item(
+    def read_setting_item(
         item_id: Annotated[str, Path(description="The setting item key to retrieve.")],
-    ):
+    ) -> dict[str, Any]:
@@
-    def open_logs():
+    def open_logs() -> dict[str, str]:
@@
-    def open_project_folder(
+    def open_project_folder(
         project_id: Annotated[
             str, Path(description="The unique identifier of the project.")
         ],
-    ):
+    ) -> dict[str, str]:
As per coding guidelines: "Maintain very high code quality in Python files, with strong typing and comprehensive test coverage."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/settings_api.py` around lines 23 - 67, Add explicit
return type annotations to the modified route handlers: annotate update_settings
to return dict[str, Any] (it returns Config.shared().settings(...)),
read_setting_item to return dict[str, Optional[Any]] (it returns {item_id:
settings.get(item_id, None)}), and annotate open_logs and open_project_folder to
return dict[str, str] (they return {"message": "opened"} or raise
HTTPException). Ensure necessary typing imports (e.g., Any, Optional, Dict) are
present and update the function signatures for update_settings,
read_setting_item, open_logs, and open_project_folder accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/desktop/studio_server/eval_api.py`:
- Around line 842-845: The OpenAPI summary for the route registered by the
`@app.get` decorator for
"/api/projects/{project_id}/tasks/{task_id}/evals/{eval_id}/run_calibration" is
out-of-date ("Run Eval Config Comparison"); update the summary parameter on that
`@app.get` route to match the new path name (e.g., "Run Calibration" or "Run Eval
Calibration") so the generated docs and UI reflect the renamed endpoint (locate
the `@app.get`(...) decorator in eval_api.py and change its summary value).
- Around line 801-816: Update the type annotation for the eval_config_id path
parameter in the set_default_eval_config function from str | None to str so the
OpenAPI/TypeScript schema correctly emits a non-nullable string; keep the
existing string-literal handling (the check if eval_config_id == "None")
unchanged, and regenerate/openapi schema as needed so clients no longer see
"string | null" for eval_config_id.

In `@app/desktop/studio_server/provider_api.py`:
- Around line 523-536: The route handler save_openai_compatible_providers is
currently accepting secrets via Query (api_key) which can leak; change the
endpoint to accept a Pydantic request body model (e.g.,
OpenAICompatibleProviderCreate with fields name, base_url, api_key) and replace
the Annotated Query parameters with a single body parameter (e.g., payload:
OpenAICompatibleProviderCreate) in the function signature, update any usages
inside the function to read payload.name/payload.base_url/payload.api_key, and
ensure the route still uses app.post("/api/provider/openai_compatible") so the
secret is passed in the request body instead of URL query params.

In
`@app/web_ui/src/routes/`(fullscreen)/setup/(setup)/connect_providers/connect_providers.svelte:
- Around line 556-562: The POST call using client.POST to
"/api/provider/docker_model_runner/connect" is passing query parameters under a
top-level "query" key so docker_model_runner_custom_url is never sent; update
the call to place the query object under params.query (i.e., replace the
top-level "query" with "params: { query: { docker_model_runner_custom_url:
docker_model_runner_custom_url || undefined } }") to match the other provider
connect calls and ensure the parameter is transmitted.

In `@libs/server/kiln_server/document_api.py`:
- Around line 1342-1359: Add an OpenAPI-visible docstring to the GET SSE
mutation handler run_extractor_config explaining that the endpoint performs
mutations and why GET is required (EventSource/SSE doesn’t support POST), and do
the same for the other GET SSE mutation handler in this module (the later SSE
GET endpoint that streams mutation results). Place the explanatory docstrings
immediately under each async def (e.g., async def run_extractor_config(...):) so
the FastAPI/OpenAPI generator includes the mutation warning and rationale in the
docs.

In `@libs/server/kiln_server/run_api.py`:
- Around line 504-507: The form description for the parameter "splits" is
misleading: it says a mapping to tag lists but the code path (parse_splits)
expects a JSON mapping of split names to numeric weights between 0 and 1. Update
the Annotated description for the splits parameter to state that it should be a
JSON string mapping split names to numeric values in the range [0,1] (or
percentages), and mention any normalization behavior if applicable so it matches
parse_splits' validation and error messages; reference the splits parameter and
the parse_splits function to locate and align the docs with the validation
contract.

In `@specs/projects/document_api/functional_spec.md`:
- Around line 27-39: The fenced code blocks containing the API snippets (e.g.,
the blocks starting with "DELETE .../runs/{run_id}", "POST
.../tasks/{task_id}/run", and "POST .../tasks/{task_id}/runs") need explicit
language identifiers to satisfy markdownlint MD040; update each opening
triple-backtick to include a language label (use "text") so the blocks become
```text and ensure all similar unlabeled code fences in this section (including
the repeated blocks around lines 27 and 42–47) are updated consistently.

In `@specs/projects/document_api/implementation_plan.md`:
- Line 2: The frontmatter contains a typo: the value for the status key is
written as "compelete"; update the frontmatter so the status key reads "status:
complete" (correct spelling) to match tooling expectations and canonical values,
ensure YAML/frontmatter formatting remains valid and, if present elsewhere in
the file, replace any other instances of "compelete" with "complete".

In `@specs/projects/document_api/project_overview.md`:
- Around line 186-194: The markdown has two issues: inconsistent task paths
(write endpoints use /task/{task_id} while reads use /tasks/{task_id}) and
malformed table blocks lacking blank lines which breaks rendering; update all
task-scoped path strings (e.g., POST /api/projects/{project_id}/task, PATCH
/api/projects/{project_id}/task/{task_id}, DELETE
/api/projects/{project_id}/task/{task_id}, POST
/api/projects/{project_id}/task/{task_id}/prompt, GET
/api/projects/{project_id}/task/{task_id}/prompts, GET
/api/projects/{project_id}/task/{task_id}/gen_prompt/{prompt_id}) to use the
plural form /tasks/{task_id} consistently, and add a blank line before and after
each table block (and move any non-table prose outside the table) in the
affected sections (including the examples around lines referenced) so
markdownlint and rendering treat tables correctly.
- Line 5: Remove the empty H1 heading (the lone "#" markdown token) from the
document to avoid creating a blank section anchor and cluttered navigation;
locate the stray "#" in specs/projects/document_api/project_overview.md and
delete that line (or replace it with an actual title if intended) so no empty
top-level heading remains.

---

Outside diff comments:
In `@app/desktop/studio_server/eval_api.py`:
- Around line 524-552: The update_eval handler currently assigns
request.train_set_filter_id into eval.train_set_filter_id without validating it;
before setting it (inside update_eval and after the existing "already set"
guard), call the same resolver used elsewhere (dataset_filter_from_id) with the
project_id and request.train_set_filter_id to ensure the filter exists/loads
successfully, and if that resolver fails or returns None raise an HTTPException
400 with a clear message; only assign eval.train_set_filter_id after successful
validation so typos or invalid IDs fail fast on PATCH.

In `@app/desktop/studio_server/settings_api.py`:
- Around line 46-77: The open_project_folder handler currently catches all
exceptions and returns HTTP 500 with detail=str(e), which masks intended
HTTPExceptions (e.g., from project_from_id) and leaks internals; change the
error handling in open_project_folder so that it does not convert existing
HTTPException instances—either remove the broad try/except or re-raise if
isinstance(e, HTTPException)—and for unexpected errors raise a new
HTTPException(status_code=500, detail="Internal server error") (do not include
str(e)); keep the call to project_from_id and open_folder unchanged so
project_from_id can raise its own 404 as designed.

In `@libs/server/kiln_server/project_api.py`:
- Around line 107-109: Guard against Config.shared().projects being None or
non-list before filtering: retrieve projects_before = Config.shared().projects,
if not isinstance(projects_before, list) then normalize it to an empty list (or
to list(projects_before) if you expect other iterables), compute projects_after
by filtering that normalized list (e.g., [p for p in normalized if p !=
str(project.path)]), and then call Config.shared().save_setting("projects",
projects_after); update the code around projects_before/projects_after to use
the normalized variable so deletion is resilient to None or wrong types.
- Around line 65-68: The code calls Project.model_validate(...) but ignores its
return value, so coercions/validators from Project aren't applied before saving;
replace the sequence that uses
updated_project.model_copy(update=project_updates) followed by a discarded
Project.model_validate(...) by validating and using the validated instance—call
Project.model_validate(...) (or updated_project.model_validate() if available)
and assign its result to updated_project, then call
updated_project.save_to_file() so the validated/coerced model is what's
persisted; reference updated_project, Project.model_validate, model_copy, and
save_to_file when making the change.

In `@libs/server/kiln_server/test_task_api.py`:
- Around line 96-104: The test's HTTPException side effect on project_from_id
isn't applied because the client.post call is outside the with patch(...)
context; move the request into the same with
patch("kiln_server.task_api.project_from_id") as mock_load: block so
mock_load.side_effect = HTTPException(...) is active when calling
client.post("/api/projects/FAKEPROJECTID/tasks", json=task_data), then assert
response.status_code and response.json()["message"] as written.

---

Nitpick comments:
In `@app/desktop/studio_server/settings_api.py`:
- Around line 23-67: Add explicit return type annotations to the modified route
handlers: annotate update_settings to return dict[str, Any] (it returns
Config.shared().settings(...)), read_setting_item to return dict[str,
Optional[Any]] (it returns {item_id: settings.get(item_id, None)}), and annotate
open_logs and open_project_folder to return dict[str, str] (they return
{"message": "opened"} or raise HTTPException). Ensure necessary typing imports
(e.g., Any, Optional, Dict) are present and update the function signatures for
update_settings, read_setting_item, open_logs, and open_project_folder
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7210cf07-3a8b-4972-9ce1-6af528a42ce4

📥 Commits

Reviewing files that changed from the base of the PR and between 7415b2a and 13cdc46.

⛔ Files ignored due to path filters (1)
  • app/web_ui/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (74)
  • app/desktop/studio_server/api_models/copilot_models.py
  • app/desktop/studio_server/copilot_api.py
  • app/desktop/studio_server/data_gen_api.py
  • app/desktop/studio_server/eval_api.py
  • app/desktop/studio_server/finetune_api.py
  • app/desktop/studio_server/import_api.py
  • app/desktop/studio_server/prompt_api.py
  • app/desktop/studio_server/prompt_optimization_job_api.py
  • app/desktop/studio_server/provider_api.py
  • app/desktop/studio_server/repair_api.py
  • app/desktop/studio_server/run_config_api.py
  • app/desktop/studio_server/settings_api.py
  • app/desktop/studio_server/skill_api.py
  • app/desktop/studio_server/test_eval_api.py
  • app/desktop/studio_server/test_prompt_api.py
  • app/desktop/studio_server/test_provider_api.py
  • app/desktop/studio_server/test_repair_api.py
  • app/desktop/studio_server/test_run_config_api.py
  • app/desktop/studio_server/test_server.py
  • app/desktop/studio_server/tool_api.py
  • app/web_ui/src/lib/api_schema.d.ts
  • app/web_ui/src/lib/stores.ts
  • app/web_ui/src/lib/stores/evals_store.ts
  • app/web_ui/src/lib/stores/prompts_store.ts
  • app/web_ui/src/lib/stores/run_configs_store.ts
  • app/web_ui/src/routes/(app)/docs/rag_configs/[project_id]/table_rag_config_row.svelte
  • app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.test.ts
  • app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.ts
  • app/web_ui/src/routes/(app)/optimize/[project_id]/[task_id]/+page.svelte
  • app/web_ui/src/routes/(app)/prompt_optimization/[project_id]/[task_id]/create_prompt_optimization_job/+page.svelte
  • app/web_ui/src/routes/(app)/prompts/[project_id]/[task_id]/create/+page.svelte
  • app/web_ui/src/routes/(app)/prompts/[project_id]/[task_id]/edit_base_prompt/+page.svelte
  • app/web_ui/src/routes/(app)/run/output_repair_edit_form.svelte
  • app/web_ui/src/routes/(app)/run/run.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/[spec_id]/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/[spec_id]/[eval_id]/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/[spec_id]/[eval_id]/[eval_config_id]/[run_config_id]/run_result/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/[spec_id]/[eval_id]/compare_run_configs/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/[spec_id]/[eval_id]/create_eval_config/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/[spec_id]/[eval_id]/eval_configs/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/compare/+page.svelte
  • app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/spec_builder/+page.svelte
  • app/web_ui/src/routes/(fullscreen)/setup/(setup)/connect_providers/connect_providers.svelte
  • app/web_ui/src/routes/(fullscreen)/setup/(setup)/create_task/edit_task.svelte
  • libs/core/kiln_ai/datamodel/basemodel.py
  • libs/core/kiln_ai/datamodel/chunk.py
  • libs/core/kiln_ai/datamodel/datamodel_enums.py
  • libs/core/kiln_ai/datamodel/embedding.py
  • libs/core/kiln_ai/datamodel/eval.py
  • libs/core/kiln_ai/datamodel/extraction.py
  • libs/core/kiln_ai/datamodel/prompt_id.py
  • libs/core/kiln_ai/datamodel/rag.py
  • libs/core/kiln_ai/datamodel/reranker.py
  • libs/core/kiln_ai/datamodel/task.py
  • libs/core/kiln_ai/datamodel/task_output.py
  • libs/core/kiln_ai/datamodel/task_run.py
  • libs/core/kiln_ai/datamodel/tool_id.py
  • libs/core/kiln_ai/datamodel/vector_store.py
  • libs/server/kiln_server/document_api.py
  • libs/server/kiln_server/project_api.py
  • libs/server/kiln_server/prompt_api.py
  • libs/server/kiln_server/run_api.py
  • libs/server/kiln_server/server.py
  • libs/server/kiln_server/spec_api.py
  • libs/server/kiln_server/task_api.py
  • libs/server/kiln_server/test_prompt_api.py
  • libs/server/kiln_server/test_spec_api.py
  • libs/server/kiln_server/test_task_api.py
  • specs/projects/document_api/architecture.md
  • specs/projects/document_api/functional_spec.md
  • specs/projects/document_api/implementation_plan.md
  • specs/projects/document_api/phase_plans/phase_3.md
  • specs/projects/document_api/phase_plans/phase_4.md
  • specs/projects/document_api/project_overview.md

Comment thread app/desktop/studio_server/eval_api.py
Comment thread app/desktop/studio_server/eval_api.py
Comment thread app/desktop/studio_server/provider_api.py
Comment thread libs/server/kiln_server/document_api.py
Comment thread libs/server/kiln_server/run_api.py
Comment thread specs/projects/document_api/functional_spec.md
@@ -0,0 +1,105 @@
---
status: compelete

@coderabbitai coderabbitai Bot Mar 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix frontmatter status typo to avoid tooling mismatch.

status: compelete is misspelled and may break any automation expecting canonical status values.

✏️ Proposed fix
-status: compelete
+status: complete
📝 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
status: compelete
status: complete
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@specs/projects/document_api/implementation_plan.md` at line 2, The
frontmatter contains a typo: the value for the status key is written as
"compelete"; update the frontmatter so the status key reads "status: complete"
(correct spelling) to match tooling expectations and canonical values, ensure
YAML/frontmatter formatting remains valid and, if present elsewhere in the file,
replace any other instances of "compelete" with "complete".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Spec file — no automation reads this field, not worth a commit.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@scosman Understood — since no tooling depends on that field, the typo is harmless. Happy to drop it.


🧠 Learnings used
Learnt from: scosman
Repo: Kiln-AI/Kiln PR: 733
File: app/web_ui/src/lib/stores/local_storage_store.ts:9-11
Timestamp: 2025-10-21T00:06:57.115Z
Learning: When scosman is refactoring code by moving it to a new location, he prefers to keep the moved code unchanged and not mix in functional improvements or bug fixes during the refactor.

Comment thread specs/projects/document_api/project_overview.md
Comment thread specs/projects/document_api/project_overview.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
app/desktop/studio_server/tool_api.py (1)

725-736: ⚠️ Potential issue | 🟡 Minor

Missing OpenAPI tag and parameter descriptions for consistency.

This endpoint is missing the tags=["Tools & MCP"] decorator and Annotated[..., Path(description=...)] parameter annotations that were added to all other tool server endpoints in this file.

🔧 Proposed fix
-    `@app.post`("/api/projects/{project_id}/tool_servers/{tool_server_id}/archive")
+    `@app.post`(
+        "/api/projects/{project_id}/tool_servers/{tool_server_id}/archive",
+        tags=["Tools & MCP"],
+    )
     async def archive_tool_server(
-        project_id: str,
-        tool_server_id: str,
+        project_id: Annotated[
+            str, Path(description="The unique identifier of the project.")
+        ],
+        tool_server_id: Annotated[
+            str, Path(description="The unique identifier of the tool server.")
+        ],
         archive_request: ToolServerArchiveRequest,
     ) -> ExternalToolServer:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/tool_api.py` around lines 725 - 736, Add the
missing OpenAPI metadata to archive_tool_server: decorate the endpoint with
tags=["Tools & MCP"] and change the parameters to use Annotated types with Path
descriptions (e.g., annotate project_id and tool_server_id with Annotated[str,
Path(description="Project ID")] and Annotated[str, Path(description="Tool server
ID")]) and ensure archive_request keeps its request body typing; update the
function signature and decorator for archive_tool_server to match the other tool
server endpoints (keeping the return type ExternalToolServer).
libs/core/kiln_ai/datamodel/basemodel.py (1)

43-44: ⚠️ Potential issue | 🟡 Minor

Remove the unused ID_FIELD constant.

The ID_FIELD constant on line 43 is no longer used after the id field was inlined on line 298. It's dead code and should be removed.

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

In `@libs/core/kiln_ai/datamodel/basemodel.py` around lines 43 - 44, Delete the
unused constant ID_FIELD (the line "ID_FIELD =
Field(default_factory=generate_model_id)") from basemodel.py; ensure no other
code references ID_FIELD and, if removing it makes generate_model_id or its
import unused, also remove the now-unreferenced import or definition to keep the
module clean.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@app/desktop/studio_server/tool_api.py`:
- Around line 725-736: Add the missing OpenAPI metadata to archive_tool_server:
decorate the endpoint with tags=["Tools & MCP"] and change the parameters to use
Annotated types with Path descriptions (e.g., annotate project_id and
tool_server_id with Annotated[str, Path(description="Project ID")] and
Annotated[str, Path(description="Tool server ID")]) and ensure archive_request
keeps its request body typing; update the function signature and decorator for
archive_tool_server to match the other tool server endpoints (keeping the return
type ExternalToolServer).

In `@libs/core/kiln_ai/datamodel/basemodel.py`:
- Around line 43-44: Delete the unused constant ID_FIELD (the line "ID_FIELD =
Field(default_factory=generate_model_id)") from basemodel.py; ensure no other
code references ID_FIELD and, if removing it makes generate_model_id or its
import unused, also remove the now-unreferenced import or definition to keep the
module clean.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 542c45c1-e1b4-4e9a-8a19-7a3c92f1a826

📥 Commits

Reviewing files that changed from the base of the PR and between 13cdc46 and a48318a.

📒 Files selected for processing (5)
  • app/desktop/studio_server/test_server.py
  • app/desktop/studio_server/tool_api.py
  • app/web_ui/src/lib/api_schema.d.ts
  • app/web_ui/src/routes/(app)/prompt_optimization/[project_id]/[task_id]/create_prompt_optimization_job/+page.svelte
  • libs/core/kiln_ai/datamodel/basemodel.py
✅ Files skipped from review due to trivial changes (1)
  • app/web_ui/src/routes/(app)/prompt_optimization/[project_id]/[task_id]/create_prompt_optimization_job/+page.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/desktop/studio_server/test_server.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
app/desktop/studio_server/tool_api.py (3)

120-121: Consider adding Field(description=...) for consistency.

Other request models in this file have been updated with Field(description=...) metadata, but ToolServerArchiveRequest was not updated. For API documentation consistency:

♻️ Optional enhancement
 class ToolServerArchiveRequest(BaseModel):
-    is_archived: bool
+    """Request to archive or unarchive a tool server."""
+
+    is_archived: bool = Field(description="Whether to archive (true) or unarchive (false) the tool server.")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/tool_api.py` around lines 120 - 121, Add a Field
description to the ToolServerArchiveRequest model so it matches other request
models: update the is_archived attribute in class ToolServerArchiveRequest to
use Field(description="...") with a short description explaining the boolean
flag (e.g., whether the tool server should be archived or unarchived) to ensure
consistent OpenAPI docs and schema metadata.

729-732: Missing Path(description=...) annotations for consistency.

Other endpoints in this file annotate project_id and tool_server_id with Annotated[str, Path(description=...)], but this endpoint uses plain str parameters.

♻️ Suggested fix for consistency
     async def archive_tool_server(
-        project_id: str,
-        tool_server_id: str,
+        project_id: Annotated[
+            str, Path(description="The unique identifier of the project.")
+        ],
+        tool_server_id: Annotated[
+            str, Path(description="The unique identifier of the tool server.")
+        ],
         archive_request: ToolServerArchiveRequest,
     ) -> ExternalToolServer:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/tool_api.py` around lines 729 - 732, The
archive_tool_server endpoint parameters project_id and tool_server_id are
missing the Annotated Path descriptions; update the function signature in
archive_tool_server to annotate both project_id and tool_server_id using
Annotated[str, Path(description="...")] (matching the description text style
used elsewhere in this file) so they mirror other endpoints' parameter metadata.

900-910: Consider removing redundant Args documentation.

The docstring's Args: section (lines 907-909) duplicates the Path(description=...) annotations. Since OpenAPI will use the Path descriptions, the docstring Args can be removed to avoid duplication and maintenance burden.

♻️ Optional cleanup
     async def get_tool_definition(
         ...
     ) -> ToolDefinitionResponse:
         """
         Get the actual OpenAI tool definition for a specific tool ID.

         This returns the real function name and parameters that would be used
         in OpenAI function calls, not the display names from ToolSetApiDescription.
-
-        Args:
-            project_id: The project ID
-            task_id: The task ID for tools that require task context
-            tool_id: The tool ID to get the definition for
         """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/tool_api.py` around lines 900 - 910, The docstring
for the "Get the actual OpenAI tool definition..." function duplicates the
Path(description=...) metadata for project_id, task_id, and tool_id; remove the
redundant "Args:" section (the lines documenting project_id, task_id, tool_id)
from that function's docstring so OpenAPI uses the Path annotations as the
single source of truth and leave only the short summary and any non-parameter
notes; locate the docstring by searching for that summary string in tool_api.py
and update it (e.g., in the function handling tool retrieval that mentions
project_id, task_id, tool_id).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/desktop/studio_server/tool_api.py`:
- Around line 120-121: Add a Field description to the ToolServerArchiveRequest
model so it matches other request models: update the is_archived attribute in
class ToolServerArchiveRequest to use Field(description="...") with a short
description explaining the boolean flag (e.g., whether the tool server should be
archived or unarchived) to ensure consistent OpenAPI docs and schema metadata.
- Around line 729-732: The archive_tool_server endpoint parameters project_id
and tool_server_id are missing the Annotated Path descriptions; update the
function signature in archive_tool_server to annotate both project_id and
tool_server_id using Annotated[str, Path(description="...")] (matching the
description text style used elsewhere in this file) so they mirror other
endpoints' parameter metadata.
- Around line 900-910: The docstring for the "Get the actual OpenAI tool
definition..." function duplicates the Path(description=...) metadata for
project_id, task_id, and tool_id; remove the redundant "Args:" section (the
lines documenting project_id, task_id, tool_id) from that function's docstring
so OpenAPI uses the Path annotations as the single source of truth and leave
only the short summary and any non-parameter notes; locate the docstring by
searching for that summary string in tool_api.py and update it (e.g., in the
function handling tool retrieval that mentions project_id, task_id, tool_id).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e84bacfc-93cc-44e7-af89-ec6bb1be519e

📥 Commits

Reviewing files that changed from the base of the PR and between a48318a and a43c71a.

📒 Files selected for processing (3)
  • app/desktop/studio_server/test_server.py
  • app/desktop/studio_server/tool_api.py
  • libs/server/kiln_server/server.py

- Move api_key from query params to JSON request body for OpenAI compatible
  provider endpoint to prevent secret leakage in logs/URLs
- Fix docker_model_runner connect call missing params.query nesting
- Fix eval_config_id path param type from str | None to str
- Update run_calibration endpoint summary to match path
- Fix splits form field description to match validation (numeric proportions)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/desktop/studio_server/eval_api.py (1)

843-845: Add explicit operation_id to align the calibration endpoint with its path.

The route handler run_eval_config_eval does not match the endpoint path /run_calibration. FastAPI derives the default operationId from the handler name, so the generated OpenAPI schema shows run_eval_config_eval as the operation identifier. Setting operation_id="run_calibration" keeps the schema semantically consistent with the endpoint path.

🛠️ Suggested fix
     `@app.get`(
         "/api/projects/{project_id}/tasks/{task_id}/evals/{eval_id}/run_calibration",
         summary="Run Calibration",
+        operation_id="run_calibration",
         tags=["Evals"],
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/desktop/studio_server/eval_api.py` around lines 843 - 845, The OpenAPI
operationId is derived from the handler name and currently becomes
"run_eval_config_eval" which doesn't match the endpoint path; update the route
decorator for the calibration endpoint (the route that uses the handler
run_eval_config_eval) to include operation_id="run_calibration" so the generated
schema uses a semantically consistent operationId that matches
"/run_calibration".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/desktop/studio_server/eval_api.py`:
- Around line 743-745: The frontend component run_eval.svelte still calls the
old backend endpoints (".../eval/.../eval_config/.../run_task_run_eval" and
".../eval/.../run_eval_config_eval"); update those fetch/XHR calls to the
renamed routes (".../evals/.../run_comparison" and
".../evals/.../run_calibration") so the run and calibration flows hit the new
backend handlers; search for any other occurrences in run_eval.svelte (and
related UI callers) and replace the old path segments ("eval/" and the two
specific action names) with "evals/" and the corresponding new action names to
avoid 404s.

---

Nitpick comments:
In `@app/desktop/studio_server/eval_api.py`:
- Around line 843-845: The OpenAPI operationId is derived from the handler name
and currently becomes "run_eval_config_eval" which doesn't match the endpoint
path; update the route decorator for the calibration endpoint (the route that
uses the handler run_eval_config_eval) to include operation_id="run_calibration"
so the generated schema uses a semantically consistent operationId that matches
"/run_calibration".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f8f05693-1f82-493a-9f8c-dccf58f95220

📥 Commits

Reviewing files that changed from the base of the PR and between a43c71a and 49154ca.

📒 Files selected for processing (6)
  • app/desktop/studio_server/eval_api.py
  • app/desktop/studio_server/provider_api.py
  • app/desktop/studio_server/test_provider_api.py
  • app/web_ui/src/lib/api_schema.d.ts
  • app/web_ui/src/routes/(fullscreen)/setup/(setup)/connect_providers/connect_providers.svelte
  • libs/server/kiln_server/run_api.py
✅ Files skipped from review due to trivial changes (1)
  • app/desktop/studio_server/test_provider_api.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/desktop/studio_server/provider_api.py
  • libs/server/kiln_server/run_api.py

Comment thread app/desktop/studio_server/eval_api.py
scosman and others added 9 commits March 30, 2026 22:32
Update 3 frontend files that still referenced old API paths after the
Phase 4 rename (eval→evals, run_config→run_configs, endpoint renames).
Fix wrong error message in task update endpoint. Correct misleading
passthrough_mimetypes and gen_type descriptions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a regression test that checks every path/query parameter in the OpenAPI
schema has a description. Fixes the 5 parameters that were missing descriptions
(gen_prompt and archive_tool_server endpoints). Also adds docstrings to 5
ambiguous endpoints (execute run, generate/save repair, eval comparison/calibration).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… docs

- Fix runtime bug: task deletion URL used old /task/ path instead of /tasks/
- Fix /api/project singular paths to /api/projects for consistency
- Remove unused ID_FIELD constant from basemodel.py
- Restore "should not be changed after creation" contract on Document.name
- Improve tautological Field descriptions across API models
- Fix docstring nits (ambiguous "model" wording, echoed descriptions)
- Fix test assertion for corrected "Failed to update task" error message
- Add trailing slash rule to API code review guidelines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ity with /run_configs/{run_config_id}

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

**Naming and path conventions:**

- **Always use plural nouns** in path segments: `/tasks/{task_id}`, never `/task/{task_id}`. Same for `/projects`, `/specs`, `/evals`, `/runs`, `/prompts`, `/documents`, `/skills`, `/run_configs`, etc. We had inconsistencies where GET used plural but POST/PATCH/DELETE used singular — this is confusing and must be caught.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would've love this to be a separate change to isolate updating API docs from changing the actual end points.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah.... to late but good point.

Comment thread app/desktop/studio_server/provider_api.py
return await connect_ollama(custom_ollama_url)

@app.get("/api/provider/docker_model_runner/connect")
@app.post(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We changed the method here from GET to POST.

Was there a reason for it being GET?

Comment thread libs/server/kiln_server/spec_api.py Outdated
@scosman scosman merged commit 3bf27aa into main Apr 1, 2026
12 checks passed
@scosman scosman deleted the scosman/improve-api-docs branch April 1, 2026 16:09
@coderabbitai coderabbitai Bot mentioned this pull request Apr 1, 2026
2 tasks
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.

4 participants