feat(api): generate Projects REST bindings - #697
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e29a894cd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
1e29a89 to
6f1aa14
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f1aa14a11
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
6f1aa14 to
054a892
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 054a892cbb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
054a892 to
37ddc3d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37ddc3da5b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
37ddc3d to
b7f9971
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b7f99719e2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
dca0f0c to
975785c
Compare
975785c to
1a8c2ed
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a8c2edf49
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
1122624 to
a2a9583
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2a9583788
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
2c10610 to
6e84ffa
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e84ffa12f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
6e84ffa to
6d56125
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6d56125486
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
| inline_models: Dict[str, Mapping[str, Any]] = {} | ||
| for method, path, operation_id, operation, path_item in _iter_operations(spec): | ||
| tags = operation.get("tags", []) | ||
| if method == "options" or not tags or tags[0] not in generated_tags: |
There was a problem hiding this comment.
Select operations by every declared tag
When an OpenAPI operation has a generated tag after another tag (for example, tags: ["Internal", "Projects"]), validation selects it because _selected_operations() checks every tag, but generation silently drops it here by checking only tags[0]. If another Projects operation exists, validation still succeeds and the resulting client is missing this operation; use the same any-tag membership predicate during collection.
Useful? React with 👍 / 👎.
6d56125 to
81b40ed
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81b40ed143
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
81b40ed to
0400981
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0400981871
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
| resolved_api_url = api_url or self._router.api_url or BraintrustEnv.API_URL.get(organization.api_url) | ||
| resolved_proxy_url = proxy_url or self._router.proxy_url or BraintrustEnv.PROXY_URL.get(organization.proxy_url) |
There was a problem hiding this comment.
Refresh routing when selecting another organization
When the same client logs in again for a different organization, these fallbacks reuse the URLs written into the router by the first login. For example, after login(org_name="one"), login(org_name="two") can return organization two while leaving client.openapi routed to organization one's API and proxy hosts. Preserve constructor-level overrides separately, but otherwise resolve each login from the newly selected organization's URLs.
Useful? React with 👍 / 👎.
| previous = generated_names.setdefault(generated_name, operation_id) | ||
| if previous != operation_id: | ||
| raise CodegenError( | ||
| f"Inline operation name collision: {previous!r} and {operation_id!r} both generate {generated_name!r}" |
There was a problem hiding this comment.
Validate collisions in emitted operation names
When two selected operation IDs differ only in acronym casing, such as getURL and getUrl, this check considers GetURL and GetUrl distinct even though _snake_case() emits get_url and GET_URL for both. The generated module then silently redefines the first operation and method, and both OPERATIONS entries reference the second operation's metadata; validate the actual method and constant identifier namespaces before generation.
Useful? React with 👍 / 👎.
Implements step 2 of #683 by adding synchronous project create, list, get, update, and delete methods, along with their public TypedDict types. Keep the initial codegen surface intentionally narrow: select one OpenAPI tag, slice the spec to its transitive component closure, and emit one operation and binding module plus one model module. Binding and inline response names are derived mechanically from operation IDs, with normalized-name collisions rejected during generation. The generated bindings reuse the existing transport, authentication, routing, retry, and error handling. Coverage includes deterministic codegen, exact wire behavior, additive responses, typing, collision validation, and a cassette-backed end-to-end Projects flow.
0400981 to
0c032d6
Compare
Implements step 2 of #683.
AI Summary
This adds the first public generated REST resource:
BraintrustOpenApiClient.projects:post_project,get_project,get_project_id,patch_project_id, anddelete_project_idbraintrust.api.types: publicCreateProject,PatchProject,Project, andGetProjectResponsetypesThe initial codegen surface is intentionally narrow: it selects one OpenAPI tag, slices the spec to that tag's transitive component closure, and emits one operation/resource module plus one model module:
The generated
ProjectsAPIresource derives public method and inline response names mechanically from operation IDs, with normalized-name collisions rejected during generation. Generated methods forward request fields and parameters without implicit defaults. Unreachable models and unselected resource placeholders are omitted; selecting a second tag requires explicit cross-resource model partitioning.Generated resources reuse the existing
ResourceAPItransport, authentication, routing, retry, and error handling.BraintrustClientowns one transport and router shared by its handwrittenauthservice and non-owningopenapiclient; both client constructors remain network-free, whileclient.auth.login()explicitly performs organization discovery and configures routing.logger.pystores this shared client and returns its OpenAPI view fromapi_client(). Generation remains offline and deterministic, and codegen drift checks replace the generated tree atomically so stale files cannot survive a layout change.Coverage includes deterministic codegen, exact wire behavior, additive responses, transparent request forwarding, typing, collision validation, and a cassette-backed end-to-end Projects flow.