API Audit: Strengthening type safety
維護者通常 1 天內回覆
@Prajna1999 已經在處理了。
開始於 2026年7月24日。
評估
這個 Issue 還沒有評估資料。
描述
Describe the current behavior
We added an automated breaking-change gate in #892 (.github/workflows/api-breaking-changes.yaml) that exports the OpenAPI schema for base vs. HEAD, diffs them with oasdiff, and blocks merges that introduce breaking changes (overridable with the breaking-change-approved label).
The gate is only as good as the schema it diffs. oasdiff can only see what OpenAPI encodes. Wherever a request/response body is typed as an opaque, permissive shape — dict[str, Any], bare Any, list[dict[str, Any]], or a route with no meaningful response_model — OpenAPI renders it as a formless object with no properties and no required list. To oasdiff, such a field looks identical before and after any change to its real contents, so genuine breaking changes pass straight through the gate undetected.
Concrete example (already happened): the credential contract.
credentialsis typedlist[dict[str, Any]]inapp/models/onboarding.pyand described as a generic "Dictionary mapping provider names to their credentials" inapp/models/credentials.py.- The actual per-provider required fields live in a runtime Python dict —
PROVIDER_CONFIGSinapp/core/providers.py— enforced imperatively byvalidate_provider_credentials(). - In #890 (2026-06-11),
provider: googlewas changed so its required fields went from["api_key"]to["api_key", "project_id", "location", "sa_key", "gcs_bucket"]. Any client previously sendingprovider: googlewith onlyapi_keynow gets rejected withMissing required fields. - This is a textbook breaking change. The oasdiff gate had already been live for 9 days (#892 merged 2026-06-02). It said nothing — because the requirement was never in the schema, it was in a dict[str, Any] plus a runtime dict.
If a discriminated union had modeled the credentials (see "solution" below), OpenAPI would have carried a real required list per provider variant and oasdiff would have flagged #890 as breaking.
Describe the enhancement you'd like
A systematic audit + hardening pass across the whole API surface:
- Inventory every loose type on the request/response boundary. Starting data from a quick grep of backend/:
dict[str, Any] / Dict[str, Any]<-- a lenient dict like this may not be the only culprit, need to check for other loose types also - Triage each occurrence into:
- Fixable → model it properly. Replace with a typed Pydantic model. For fields whose shape depends on a discriminator (the credentials case), use a Pydantic discriminated union so OpenAPI emits oneOf + discriminator, each variant carrying its own required fields:
class GoogleCredentials(BaseModel):
provider: Literal["google"]
api_key: str
project_id: str
location: str
sa_key: dict
gcs_bucket: str
class OpenAICredentials(BaseModel):
provider: Literal["openai"]
api_key: str
# ...
Credential = Annotated[
Union[GoogleCredentials, OpenAICredentials, ...],
Field(discriminator="provider"),
]
- Genuinely dynamic → document + guard another way. Some payloads are legitimately free-form (arbitrary provider passthrough, user-defined metadata blobs). For these, oasdiff structurally can't help. Add a golden-file contract test instead: snapshot the effective contract (e.g. PROVIDER_CONFIGS → a checked-in providers.contract.json) and assert equality in CI, so any change fails the test with a visible diff and forces explicit review.
- Prevent regressions. Decide on a lint/CI rule that discourages introducing new dict[str, Any] / bare Any on request/response models without an explicit, reviewed opt-out, so the audit doesn't silently rot back.
Why is this enhancement needed?
- Correctness of the guardrail: right now the breaking-change gate gives false confidence: a green check does not mean "no breaking changes," it means "no breaking changes in the parts of the contract we bothered to type strictly." #890 is proof of the blind spot and we have already faced the consequences.
- Client trust: downstream consumers rely on our contracts, silent breaking failures surface as production failures
- 主要語言
- Python
- 星號
- 18
- 分支
- 11
- 平均合併
- 2 天 16 小時
- 30 天內合併 PR
- 26
環境準備
- 提供 Dockerfile 或 Docker Compose 檔案
- 有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
ProjectTech4DevAI/kaapi-backend 的其他 Issue
-
bug
難度 2/5 1-3 小時 新手友好度 68/100
ProjectTech4DevAI/kaapi-backend#889 ·
維護者通常 1 天內回覆
-
Otel: Standardize key constants可能已有人在做 @vprashrex 於 1 天前認領。 未關閉
ProjectTech4DevAI/kaapi-backend#1294 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
ECS: Automate task definition creation可能已有人在做 @Ayush8923 於 1 天前認領。 未關閉infrastructure
ProjectTech4DevAI/kaapi-backend#1293 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
Chabot: Server-side chatbot state management可能已有人在做 @Ayush8923 於 1 天前認領。 未關閉
ProjectTech4DevAI/kaapi-backend#1278 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
Models: Update default provider models可能已有人在做 @Prajna1999 於 2 天前認領。 未關閉
ProjectTech4DevAI/kaapi-backend#1272 · 已指派 1 人 ·
維護者通常 1 天內回覆
查看 ProjectTech4DevAI/kaapi-backend 的全部 Issue
相似的 Issue
-
bug good first issue
難度 2/5 1-3 小時 新手友好度 78/100
yunaremaia/proto-drift#22 ·
-
bug good first issue
難度 2/5 1-3 小時 新手友好度 82/100
yunaremaia/memwatch#96 ·
-
bug good first issue
難度 1/5 1 小時以內 新手友好度 91/100
yunaremaia/ci-gate-watch#34 ·
-
bug good first issue
難度 2/5 1-3 小時 新手友好度 85/100
yunaremaia/agent-capability-attestation#64 ·
維護者通常 1 天內回覆
-
bug good first issue
難度 1/5 1 小時以內 新手友好度 86/100
yunaremaia/agent-call-graph#43 ·
維護者通常 1 天內回覆