Skip to content

docs: populate OpenAPI metadata, examples, and error responses (#18) - #67

Open
Fury03 wants to merge 2 commits into
Deen-Bridge:devfrom
Fury03:docs/issue-18-openapi-docs
Open

docs: populate OpenAPI metadata, examples, and error responses (#18)#67
Fury03 wants to merge 2 commits into
Deen-Bridge:devfrom
Fury03:docs/issue-18-openapi-docs

Conversation

@Fury03

@Fury03 Fury03 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Closes #18

What

/docs is the primary integration reference for the backend and frontend teams, but the app gave FastAPI only a title — so it rendered untagged, unsummarized routes with no field descriptions and only happy-path schemas.

App metadata — description covering session semantics and the response envelope, version, contact, license, and a tag list with descriptions covering every router (chat, health, tafsir, study, stellar, scholar-review).

Every route now carries a tag and a summary, plus a docstring explaining what the schema can't — above all that omitting chat_id starts a session and passing it back continues one.

/chat documents 200, 422 and 500 with example bodies. ChatRequest ships two worked request examples that Swagger's dropdown picks up: "Start a new session" (no chat_id) and "Continue a session".

DELETE /chat/{chat_id} and /ping declare typed response models. The delete route also documents its idempotent case — deleting an unknown session returns 200 with a "not found" message, not a 404 — and its path parameter is described.

Every field on ChatRequest, ChatResponse and Message has a description and, where scalar, an example.

README links /docs and adds a copy-pasteable two-step curl example: start a session, then continue it with the returned chat_id, then delete it.

No runtime behavior change

This is documentation metadata only — no handler was touched.

One judgement call worth flagging: /ping returns a Python set literal, which FastAPI serializes to a one-element JSON array, not an object. I gave it a RootModel[List[str]] that documents what it actually returns rather than a tidier {"status": "ok"}, because changing the body would break any client asserting on it and the issue explicitly requires identical responses. If you'd like /ping reshaped, that's a behavior change worth its own PR and I'm happy to open one.

I verified this rather than assuming it: I captured live responses for /ping, /chat (422), DELETE /chat/{id}, /cache/stats, /confidence/policy, /tafsir/sources and /tafsir (400) on dev, then again on this branch — byte-identical.

Tests

tests/test_openapi_docs.py (27 tests) asserts the acceptance criteria against the generated schema, so the docs can't rot silently: every route is tagged and summarized, every declared tag is used and every used tag is declared, /chat documents all three statuses each with an example, delete and ping expose typed schemas, and every model field has a description (and scalars an example). It also checks the "start a session" example genuinely omits chat_id — an example that contradicts its own caption would be worse than none.

main.py configures the Gemini SDK at import, so the suite stubs it before importing — no API key, no model calls. CI extends flake8/compile and adds the new pytest step. Full suite: 489 passed, 1 skipped.

Summary by CodeRabbit

  • Documentation

    • Expanded the README API section with direct Swagger UI (/docs) and OpenAPI (/openapi.json) links.
    • Added detailed guidance for starting, continuing, and ending chat sessions, including chat_id usage and idempotent delete behavior.
    • Documented the optional response blocks and clarified what clients can rely on.
  • Improvements

    • Refreshed OpenAPI documentation and enriched request/response schemas with descriptions, examples, and endpoint metadata.
  • Tests

    • Added automated OpenAPI contract tests ensuring tags, summaries, examples, and chat-session semantics match the documented interface.
  • Chores

    • Updated CI to lint and run the OpenAPI documentation test suite.

/docs is the primary integration reference for the backend and frontend
teams, but the app supplied only a title, so it showed untagged,
unsummarized routes with no field descriptions and only happy-path
schemas.

- App metadata: description covering session semantics and the response
  envelope, version, contact and license, and a tag list with descriptions
  covering every router (chat, health, tafsir, study, stellar,
  scholar-review).
- Every route now carries a tag and a summary, with docstrings explaining
  the semantics that are not obvious from the schema — above all that
  omitting chat_id starts a session and passing it back continues one.
- /chat documents 200, 422 and 500 with example bodies, and ChatRequest
  ships two worked request examples (start a session, continue one).
- DELETE /chat/{chat_id} and /ping declare typed response models, and the
  delete route documents its idempotent "not found" case, which also
  returns 200.
- Every field on ChatRequest, ChatResponse and Message has a description
  and, where scalar, an example.
- README links /docs and adds a copy-pasteable two-step curl example:
  start a session, then continue it with the returned chat_id.

Documentation-only: no handler was touched. PingResponse deliberately
documents the array that /ping actually returns rather than a tidier
shape, so no response body changes. Verified by diffing live responses
for /ping, /chat 422, delete, /cache/stats, /confidence/policy and
/tafsir against dev — byte-identical.

tests/test_openapi_docs.py asserts the acceptance criteria against the
generated schema, so the docs cannot rot silently.

Closes Deen-Bridge#18
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c5d173c7-3c3e-403d-b201-de6aeed6e35f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The API now exposes richer OpenAPI metadata, typed response schemas, field descriptions, examples, and session guidance. README documentation explains interactive docs and chat lifecycle usage, while dedicated tests and CI steps validate the generated schema.

Changes

OpenAPI Documentation

Layer / File(s) Summary
API metadata and schema contracts
main.py
FastAPI metadata and Pydantic models now include descriptions, examples, tags, session semantics, and typed response shapes.
Endpoint documentation
main.py
Chat, delete, ping, cache, and confidence routes declare summaries, tags, response models, examples, and parameter metadata.
OpenAPI contract tests
tests/test_openapi_docs.py, .github/workflows/ci.yml
Tests validate generated metadata, route documentation, examples, schemas, and field descriptions; CI linting and execution cover the new test module.
Interactive and conversation documentation
README.md
README instructions cover /docs, /openapi.json, chat continuation using chat_id, and idempotent chat deletion.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the PR’s main documentation-focused OpenAPI changes.
Linked Issues check ✅ Passed The changes satisfy the linked issue’s requirements for OpenAPI metadata, tagged summaries, examples, response models, README updates, and no behavior change.
Out of Scope Changes check ✅ Passed The CI, README, tests, and FastAPI schema updates all directly support the documented OpenAPI improvements, with no obvious unrelated scope creep.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main.py`:
- Around line 252-271: The ChatRequest model’s json_schema_extra examples must
be plain request payloads rather than OpenAPI Example Object wrappers; update
the examples in ChatRequest and adjust tests/test_openapi_docs.py accordingly.
If preserving summaries and descriptions, move those labeled variants to the
/chat request-body documentation and assert them there, while validating the
model schema against unwrapped payload examples.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b175aa10-a448-410d-bf26-f089d3c0d1d6

📥 Commits

Reviewing files that changed from the base of the PR and between 06981f2 and 0e6abd3.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • README.md
  • main.py
  • tests/test_openapi_docs.py

Comment thread main.py
JSON Schema `examples` take instances of the model itself, so the
{summary, description, value} wrappers on ChatRequest were rendering the
wrapper in the schema view instead of a usable request body.

The plain payloads now live on the model, and the labelled variants a
reader picks between — "Start a new session" and "Continue a session" —
move to the /chat operation as OpenAPI Example Objects via
Body(openapi_examples=...), which is where Swagger's example dropdown
reads them from.

Tests assert both shapes: that schema examples carry no Example Object
wrapper keys and only real model fields, and that the operation's
labelled examples exist, with the "start" one genuinely omitting chat_id
and the "continue" one genuinely sending it.

Responses remain byte-identical to dev.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
main.py (1)

213-221: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make chat_id validation match the session contract. ChatRequest.chat_id accepts any string, and an unknown ID silently creates a new chat under that caller-provided value. That makes typos look like valid continuations and leaves the API contract misleading. Constrain chat_id to the expected session-id format and return a documented 4xx for unknown IDs, or update the schema to say arbitrary IDs are accepted and will start a new session.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 213 - 221, Update ChatRequest.chat_id and the session
lookup flow to enforce the documented session contract: validate the expected
session-ID format and return a documented 4xx when the ID is unknown, rather
than silently creating a session under it. Ensure the description and examples
remain consistent with this behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main.py`:
- Around line 540-544: Update the safety-disabled branch in chat so the
synchronous generate()/send_message() model call does not execute on the event
loop; offload it to an appropriate worker thread or use an async client while
preserving the existing response behavior.

---

Outside diff comments:
In `@main.py`:
- Around line 213-221: Update ChatRequest.chat_id and the session lookup flow to
enforce the documented session contract: validate the expected session-ID format
and return a documented 4xx when the ID is unknown, rather than silently
creating a session under it. Ensure the description and examples remain
consistent with this behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e895f2d5-b156-4c8e-bdbe-2f4615cecb1f

📥 Commits

Reviewing files that changed from the base of the PR and between 0e6abd3 and d9c1466.

📒 Files selected for processing (2)
  • main.py
  • tests/test_openapi_docs.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_openapi_docs.py

Comment thread main.py
Comment on lines +540 to +544
async def chat(
http_request: Request,
fastapi_response: Response,
request: ChatRequest = Body(..., openapi_examples=CHAT_REQUEST_EXAMPLES),
):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline main.py --items all --match 'chat|generate'
rg -n -C 4 'SAFETY_PIPELINE_ENABLED|def generate|send_message\(|run_async\(' main.py
fd -HI -t f '^(pyproject\.toml|requirements.*\.txt|setup\.py|Pipfile)$' . -0 |
  xargs -0 -r grep -nE 'google-generativeai|google-genai|fastapi'

Repository: Deen-Bridge/dnb-ai

Length of output: 1804


Offload the synchronous model call on the safety-disabled path. When SAFETY_PIPELINE_ENABLED is false, generate() is called directly and reaches send_message() on the event loop, which blocks this worker during model round-trips. Use an async client or offload the call to keep concurrent requests moving.

🧰 Tools
🪛 Ruff (0.15.21)

[warning] 543-543: Do not perform function call Body in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 540 - 544, Update the safety-disabled branch in chat so
the synchronous generate()/send_message() model call does not execute on the
event loop; offload it to an appropriate worker thread or use an async client
while preserving the existing response behavior.

Source: Path instructions

@Fury03

Fury03 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — the first finding is fixed in d9c1466; the second is real but I've deliberately left it out of this PR.

Schema vs. operation examples — fixed. You were right: JSON Schema examples take instances of the model, so the {summary, description, value} wrappers were rendering the wrapper in the schema view instead of a usable body. The plain payloads now live on ChatRequest, and the labelled variants moved to the operation via Body(openapi_examples=...), which is where Swagger's dropdown reads them. Confirmed in the generated spec:

schema examples:    [{"prompt": "What are the conditions of wudu?"}, {"prompt": "...", "chat_id": "...", "madhhab": "shafii"}]
operation examples: {"start_session": "Start a new session", "continue_session": "Continue a session"}

Tests assert both shapes — that schema examples carry no Example Object keys and only real model fields, and that the labelled "start" example genuinely omits chat_id while "continue" genuinely sends it.

Blocking send_message() — valid, but out of scope here, and worse than described. I checked before deciding: SafetyPipeline.run_async only offloads classification. _complete() calls generator(...) synchronously as well, so the model round-trip blocks the event loop on both paths, not just when SAFETY_PIPELINE_ENABLED is false. It's pre-existing on dev and unrelated to anything this PR touches.

I'm not fixing it here because #18 states this is a "pure documentation-metadata change — behavior must not change; don't refactor handlers while in there," and the PR's central claim is that responses are byte-identical to dev. Quietly changing the concurrency behaviour of the hottest path in the service under a docs PR would make that claim false and give the change no focused review.

@zeemscript happy to open it as its own PR — the fix is to offload the generator call inside SafetyPipeline._complete so both paths benefit, with a test that the event loop stays responsive during generation. Say the word and I'll pick it up.

@Fury03

Fury03 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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