Skip to content

Conversation

@tothandras
Copy link
Contributor

@tothandras tothandras commented Oct 26, 2025

Summary by CodeRabbit

  • New Features

    • Go client now provides strongly-typed response handling for all API operations
    • Python client updated with improved endpoint configuration and token-based authentication
  • Documentation

    • Added comprehensive sync and async Python usage examples covering customers, subscriptions, entitlements, and events
    • Enhanced schema documentation with clearer pricing example payloads
    • Updated Python README with setup instructions and usage patterns

@tothandras tothandras added the release-note/feature Release note: Exciting New Features label Oct 26, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 26, 2025

Warning

Rate limit exceeded

@tothandras has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 1 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between f630c5b and 5331d32.

⛔ Files ignored due to path filters (19)
  • api/client/python/openmeter/_generated/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_client.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_configuration.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_utils/model_base.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_client.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_configuration.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_enums.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_models.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/_patch.py is excluded by !**/_generated/**
📒 Files selected for processing (9)
  • api/client/python/examples/async/customer.py (1 hunks)
  • api/client/python/examples/async/entitlement.py (1 hunks)
  • api/client/python/examples/async/query.py (1 hunks)
  • api/client/python/examples/async/subscription.py (1 hunks)
  • api/client/python/examples/sync/customer.py (1 hunks)
  • api/client/python/examples/sync/entitlement.py (1 hunks)
  • api/client/python/examples/sync/subscription.py (1 hunks)
  • api/client/python/openmeter/_types.py (1 hunks)
  • api/client/python/pyproject.toml (1 hunks)
📝 Walkthrough

Walkthrough

This PR regenerates client libraries across Go, Python, and JavaScript by updating OpenAPI schemas, TypeSpec configurations, and client generators. The Go client replaces request functions with response-parsing variants (WithResponse suffix). Python client is restructured with new wrapper clients, type system, configuration, and examples replacing old autorest-generated code. TypeSpec patches and configurations are updated to support generation. Documentation and build configuration are enhanced.

Changes

Cohort / File(s) Summary
Go Client Regeneration
api/client/go/client.gen.go
Extensive function renames and regeneration: replaces ~60 request functions with WithResponse variants that parse responses into typed structs and handle status codes. Each new function constructs request, applies editors, executes via client, and unmarshals JSON response. Includes embedded Swagger spec decoding logic.
OpenAPI Schema Examples
api/openapi.yaml, api/openapi.cloud.yaml
Updates Price schema example from empty object {} to structured object with type: flat, amount: '100', paymentTerm: in_arrears for improved documentation clarity.
JavaScript Client
api/client/javascript/src/client/schemas.ts
Updates InvoiceLine interface price property example from {} to detailed example object with flat pricing structure.
Python Configuration & Build
api/client/python/.gitattributes, .gitignore, MANIFEST.in, pyproject.toml, config.yaml
Updates gitattributes pattern from src/**/* to openmeter/**/*; reorganizes .gitignore with expanded packaging ignores; adds packaging file inclusions (*.md, LICENSE, openmeter/py.typed); removes autorest config.yaml entirely; updates dependencies from azure-core to corehttp, adds typing-extensions and urllib3; adds Black configuration.
Python Client Core
api/client/python/openmeter/__init__.py, _client.py, _types.py, _version.py, models/__init__.py, operations/__init__.py, aio/__init__.py, aio/_client.py, aio/operations/__init__.py
New wrapper client architecture: Client class with token-based auth support; new _types.py with comprehensive type aliases for API models; _version.py with VERSION constant; patch integration via _patch_sdk() calls; dynamic all population with TYPE_CHECKING guards.
Python Old Code Removal
api/client/python/src/openmeter/__init__.py, _client.py, _configuration.py, _operations/__init__.py, aio/__init__.py, aio/_client.py, aio/_configuration.py, aio/_vendor.py
Removes entire autorest-generated client infrastructure including Client class, ClientConfiguration, _operations mixin, pipeline setup, and vendor typing helpers.
Python Async Examples
api/client/python/examples/async/{ingest,query,customer,entitlement,subscription}.py, api/client/python/examples/async/pyproject.toml
Adds 5 new async example scripts demonstrating client usage for ingesting events, querying meters, managing customers, checking entitlements, and handling subscriptions. Removes redundant pyproject.toml from async subdirectory.
Python Sync Examples
api/client/python/examples/sync/{ingest,query,customer,entitlement,subscription}.py
Adds 5 new synchronous example scripts mirroring async examples with blocking API calls. Uses environment-based configuration with sensible defaults.
Python Documentation
api/client/python/README.md, examples/README.md, examples/pyproject.toml
Rewrites README with updated Quickstart, Examples/Setup structure, Event model usage (replacing CloudEvent), and meter query examples; adds examples/README.md with Poetry setup instructions; adds examples/pyproject.toml with Python ^3.9 requirement and openmeter dependency.
TypeSpec Configuration & Patches
api/spec/package.json, tspconfig.yaml, tspconfig.client.yaml, patches/@typespec__openapi.patch
Updates TypeSpec versions to 1.5.0; adds @typespec/http-client-python (0.19.1); adds onlyBuiltDependencies; adds client-dir and generation-subdir parameters; updates OpenAPI patch: changes helpers.js shouldInline logic to remove Scalar case, updates dependencies, adds scripts section.
TypeSpec Source
api/spec/src/query.tsp
Removes unused TypeSpec imports: using TypeSpec.Rest; and using TypeSpec.OpenAPI;.
Development
flake.nix
Adds uv.enable = true under Python language configuration in devenv shell.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

Areas requiring extra attention:

  • Go client response parsing logic: Verify error handling, status code checks, and JSON unmarshaling correctness across ~60 functions—pattern consistency critical due to large repetition count
  • Python client initialization and authentication: Ensure ServiceKeyCredential and authentication_policy setup works correctly for both sync and async clients; verify token handling and defaults
  • TypeSpec patch modifications (@typespec__openapi.patch): Review removal of Scalar case in shouldInline logic and its impact on code generation; validate generated output consistency
  • Python type aliases and imports: Validate _types.py Union definitions, forward references, and TYPE_CHECKING guarding for circular dependency prevention
  • Python examples correctness: Verify async/sync examples use correct API signatures, error handling, and environment variable patterns; test with different configurations
  • Dependency migration: Ensure corehttp replaces azure-core correctly; validate no broken imports or incompatible API changes in client initialization and request handling

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat: update python SDK" is related to the changeset as it accurately describes a significant portion of the changes. The raw summary shows extensive Python SDK modifications including new package structure, wrapper client classes, type definitions, multiple new example files, and dependency updates. However, the changeset also includes substantial changes to the Go client (extensive function renaming with "WithResponse" suffix across dozens of functions) and JavaScript client, plus infrastructure changes to OpenAPI specs and TypeSpec configurations. The title captures the Python SDK updates but does not reflect the equally significant Go SDK refactoring or the broader scope of multi-language SDK updates in this changeset.

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.

@tothandras tothandras force-pushed the feat/python-sdk-generator branch 2 times, most recently from 97b91a8 to 74f7751 Compare October 27, 2025 14:46
@tothandras tothandras marked this pull request as ready for review October 27, 2025 18:07
@tothandras tothandras requested a review from a team as a code owner October 27, 2025 18:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🧹 Nitpick comments (24)
api/client/python/.gitignore (1)

3-7: Consider consistent trailing slashes for directory patterns.

While functional, adding trailing slashes to directory entries (e.g., *.egg-info/, dist/, build/) would improve consistency with other directory patterns like .venv/ and venv/ below. This is a stylistic convention that makes intent clearer.

Example cleanup:

-*.egg-info/
-*.egg
-*.eggs/
-dist/
-build/
+*.egg-info/
+*.egg/
+*.eggs/
+dist/
+build/
flake.nix (1)

51-53: LGTM! Modern Python tooling enabled.

Enabling uv is a great addition for faster package management. It integrates well with devenv and will improve the development experience for the Python SDK work.

Note: Both poetry (line 133) and uv are now present. They can coexist, though they serve somewhat overlapping purposes. If the project standardizes on uv for all Python package management in the future, poetry could potentially be removed, but this is not urgent.

api/spec/tspconfig.yaml (1)

13-21: Verify placeholder version and packaging file generation strategy.

The Python emitter configuration is well-structured, but two design decisions need verification:

  1. Placeholder version (Line 18): package-version: '0.0.0' is a placeholder. Confirm whether this should be:

    • Replaced with actual SDK version from a shared source (e.g., pyproject.toml, __init__.py)
    • Updated via CI/CD pipeline before generation
    • Left as-is intentionally for development
  2. Disabled packaging files (Line 19): generate-packaging-files: false implies pyproject.toml and other packaging artifacts are managed separately. Confirm this is intentional and that generated Python models don't need regeneration during releases.

api/client/go/client.gen.go (1)

44250-44682: Prefer go:embed gzip blob over thousands of base64 string literals

Embedding the spec as a massive []string of base64 chunks hurts compile time, binary size, and diff readability. Recommend switching the generator/template to ship a gzipped spec via //go:embed and decode once with sync.Once.

Example (illustrative):

//go:build !tools
package client

import (
  "bytes"
  "compress/gzip"
  "embed"
  "io"
  "sync"
)

//go:embed swagger.json.gz
var swaggerSpecGz []byte

var (
  swaggerOnce sync.Once
  swaggerJSON []byte
  swaggerErr  error
)

func GetSwagger() ([]byte, error) {
  swaggerOnce.Do(func() {
    r, err := gzip.NewReader(bytes.NewReader(swaggerSpecGz))
    if err != nil { swaggerErr = err; return }
    defer r.Close()
    swaggerJSON, swaggerErr = io.ReadAll(r)
  })
  return swaggerJSON, swaggerErr
}

This keeps the source lean, speeds builds, and avoids enormous generated diffs. If this file is auto-generated, make the change in the generator.

api/client/python/examples/sync/query.py (3)

3-3: Remove unused import.

The json module is imported but never used in this script.

-import json

6-6: Remove unused import.

The FilterString model is imported but never used in this script.

-from openmeter.models import MeterQueryResult, FilterString
+from openmeter.models import MeterQueryResult

15-18: Use context manager for proper resource cleanup.

The client should be used with a context manager or explicitly closed to ensure proper cleanup of resources.

Apply this diff to use a context manager:

-client = OpenMeterCloudClient(
-    endpoint=ENDPOINT,
-    credential=credential,
-)
-
-
 def main() -> None:
-    try:
+    with OpenMeterCloudClient(
+        endpoint=ENDPOINT,
+        credential=credential,
+    ) as client:
+        try:
-        # Query total values
-        r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total")
+            # Query total values
+            r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total")

And indent the rest of the try block accordingly.

api/client/python/examples/sync/ingest.py (1)

16-19: Use context manager for proper resource cleanup.

The client should be used with a context manager or explicitly closed to ensure proper cleanup of resources.

Apply this diff to use a context manager:

-client = OpenMeterCloudClient(
-    endpoint=ENDPOINT,
-    credential=credential,
-)
-
-
 def main() -> None:
-    try:
+    with OpenMeterCloudClient(
+        endpoint=ENDPOINT,
+        credential=credential,
+    ) as client:
+        try:
-        # Create a CloudEvents event
-        event = Event(
+            # Create a CloudEvents event
+            event = Event(

And indent the rest of the try block accordingly.

api/client/python/README.md (5)

3-3: Fix the reversed Markdown link.

Current syntax shows the URL as the label. Swap label and href.

-[https://pypi.org/project/openmeter](On PyPI)
+[On PyPI](https://pypi.org/project/openmeter)

5-10: Typos and naming: “Prerequisites” and “TypeSpec”.

  • “Prequisites” → “Prerequisites”
  • “Typespec” → “TypeSpec”
-This package is generated by `@typespec/http-client-python` with Typespec.
-## Prequisites
+This package is generated by `@typespec/http-client-python` with TypeSpec.
+## Prerequisites

24-33: Missing imports in Setup snippet.

Users copying the sample will hit NameError for Event/uuid/datetime. Include them explicitly.

-from openmeter import OpenMeterCloudClient
-from corehttp.credentials import ServiceKeyCredential
+import datetime
+import uuid
+from openmeter import OpenMeterCloudClient
+from corehttp.credentials import ServiceKeyCredential
+from openmeter.models import Event

38-55: Event payload example: keep consistent with other examples.

Async example uses "tokens" key; README uses "value". Pick one to avoid confusion; consider "tokens" since the query example references a tokens meter.


60-63: Type annotation requires import.

Either import MeterQueryResult or drop the annotation to keep the snippet minimal.

-# Query total values
-r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total")
-print("Query total values:", r.data[0].value)
+from openmeter.models import MeterQueryResult
+# Query total values
+r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total")
+print("Query total values:", r.data[0].value)
api/client/python/openmeter/models/__init__.py (2)

368-371: Avoid wildcard import pollution from patch module.

from ._patch import * at runtime pollutes the namespace. If possible, import only names in _patch_all or explicitly import patch_sdk to reduce surface.

Example (pattern):

# instead of star import
from ._patch import patch_sdk as _patch_sdk
from ._patch import __all__ as _patch_all
for name in _patch_all:
    globals()[name] = getattr(__import__(f"{__name__}._patch", fromlist=[name]), name)

727-728: Guard patch execution and all extension.

If _patch_all is empty or patch raises, import-time side effects can break consumers. Add try/except and type checks.

-__all__.extend([p for p in _patch_all if p not in __all__])  # pyright: ignore
-_patch_sdk()
+if isinstance(_patch_all, (list, tuple)):
+    __all__.extend([p for p in _patch_all if p not in __all__])  # pyright: ignore
+try:
+    _patch_sdk()
+except Exception:  # best-effort patching
+    pass
api/client/python/examples/async/ingest.py (2)

12-16: Handle missing OPENMETER_TOKEN.

ServiceKeyCredential(None) will likely fail. Fail fast with a clear message.

-ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud"
-token: Optional[str] = environ.get("OPENMETER_TOKEN")
-
-credential = ServiceKeyCredential(token)
+ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud"
+token: Optional[str] = environ.get("OPENMETER_TOKEN")
+if not token:
+    raise RuntimeError("OPENMETER_TOKEN is not set")
+credential = ServiceKeyCredential(token)

42-44: Optional: enrich error output.

Consider printing e.status_code and e.model to aid debugging.

-except HttpResponseError as e:
-    print(f"Error ingesting event: {e}")
+except HttpResponseError as e:
+    detail = getattr(e, "model", None)
+    print(f"Error ingesting event: {getattr(e, 'status_code', 'n/a')} {e}. Details: {detail}")
api/client/python/openmeter/_utils/serialization.py (3)

824-825: Avoid eval for basic type casting.

eval(data_type) is unnecessary and risky. Prefer explicit mapping.

-        return eval(data_type)(data)  # nosec # pylint: disable=eval-used
+        cast_map = {"int": int, "float": float, "bool": bool}
+        return cast_map[data_type](data)

1069-1082: Time serialization: microseconds formatting.

Use zero-padded microseconds and trim trailing zeros for cleaner ISO output.

-        t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second)
-        if attr.microsecond:
-            t += ".{:02}".format(attr.microsecond)
+        t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second)
+        if attr.microsecond:
+            micros = str(attr.microsecond).rjust(6, "0").rstrip("0")
+            t += f".{micros}"

1104-1110: Naive datetime handling warning only.

Consider normalizing naive datetimes to UTC to avoid localtime ambiguity.

-            if not attr.tzinfo:
-                _LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
-            utc = attr.utctimetuple()
+            if not attr.tzinfo:
+                _LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
+                attr = attr.replace(tzinfo=TZ_UTC)
+            utc = attr.utctimetuple()
api/client/python/openmeter/_utils/model_base.py (3)

317-326: Harden type-alias detection for Python 3.12+/PEP 695.

typing._GenericAlias is private and insufficient for types.GenericAlias/UnionType.

Apply:

+import types as _types_mod
@@
-    types = {
+    types_map = {
         k: v
-        for k, v in sys.modules[module_name].__dict__.items()
-        if isinstance(v, typing._GenericAlias)  # type: ignore
+        for k, v in sys.modules[module_name].__dict__.items()
+        if isinstance(
+            v,
+            tuple(
+                t
+                for t in (
+                    getattr(typing, "_GenericAlias", None),
+                    getattr(_types_mod, "GenericAlias", None),
+                )
+                if t is not None
+            ),
+        )
     }
-    if alias_name not in types:
+    # Support PEP 604 unions (e.g., X | Y) which appear as types.UnionType
+    types_map.update(
+        {
+            k: v
+            for k, v in sys.modules[module_name].__dict__.items()
+            if type(v).__name__ == "UnionType"
+        }
+    )
+    if alias_name not in types_map:
         return alias_name
-    return types[alias_name]
+    return types_map[alias_name]

125-160: Option: preserve Decimal precision in JSON.

Converting Decimal to float can lose precision for monetary values. Consider serializing as string when format requires exactness.

Apply:

-            if isinstance(o, decimal.Decimal):
-                return float(o)
+            if isinstance(o, decimal.Decimal):
+                # Preserve precision; downstream can parse if needed
+                return str(o)

If you keep floats, consider a per-field format="str" to opt-in precision.


100-116: Unix timestamp serialization: ensure timezone-awareness.

When format == "unix-timestamp", naive datetimes are interpreted as UTC via utctimetuple(). Explicitly localize naive values or reject them to avoid silent misinterpretation.

Apply:

-        if format == "unix-timestamp":
-            return int(calendar.timegm(o.utctimetuple()))
+        if format == "unix-timestamp":
+            dt = o if o.tzinfo else o.replace(tzinfo=TZ_UTC)
+            return int(calendar.timegm(dt.utctimetuple()))
api/client/python/openmeter/_types.py (1)

3-9: Optional: declare aliases explicitly with TypeAlias.

Improves readability and tooling.

Apply:

-from typing import TYPE_CHECKING, Union
+from typing import TYPE_CHECKING, Union, TypeAlias
@@
-App = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"]
+App: TypeAlias = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"]
@@
-NotificationChannel = "_models.NotificationChannelWebhook"
+NotificationChannel: TypeAlias = "_models.NotificationChannelWebhook"

Replicate for other aliases as desired.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d0d417 and 74f7751.

⛔ Files ignored due to path filters (4)
  • api/client/python/examples/async/poetry.lock is excluded by !**/*.lock
  • api/client/python/examples/poetry.lock is excluded by !**/*.lock
  • api/client/python/poetry.lock is excluded by !**/*.lock
  • api/spec/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • api/client/go/client.gen.go (1 hunks)
  • api/client/javascript/src/client/schemas.ts (1 hunks)
  • api/client/python/.gitattributes (1 hunks)
  • api/client/python/.gitignore (1 hunks)
  • api/client/python/MANIFEST.in (1 hunks)
  • api/client/python/README.md (2 hunks)
  • api/client/python/config.yaml (0 hunks)
  • api/client/python/examples/README.md (1 hunks)
  • api/client/python/examples/async/ingest.py (1 hunks)
  • api/client/python/examples/async/pyproject.toml (0 hunks)
  • api/client/python/examples/async/query.py (1 hunks)
  • api/client/python/examples/pyproject.toml (1 hunks)
  • api/client/python/examples/sync/ingest.py (1 hunks)
  • api/client/python/examples/sync/query.py (1 hunks)
  • api/client/python/openmeter/__init__.py (1 hunks)
  • api/client/python/openmeter/_client.py (1 hunks)
  • api/client/python/openmeter/_configuration.py (1 hunks)
  • api/client/python/openmeter/_patch.py (1 hunks)
  • api/client/python/openmeter/_types.py (1 hunks)
  • api/client/python/openmeter/_utils/model_base.py (1 hunks)
  • api/client/python/openmeter/_utils/serialization.py (71 hunks)
  • api/client/python/openmeter/_version.py (1 hunks)
  • api/client/python/openmeter/aio/__init__.py (1 hunks)
  • api/client/python/openmeter/aio/_client.py (1 hunks)
  • api/client/python/openmeter/aio/_configuration.py (1 hunks)
  • api/client/python/openmeter/aio/_patch.py (1 hunks)
  • api/client/python/openmeter/aio/operations/__init__.py (1 hunks)
  • api/client/python/openmeter/aio/operations/_patch.py (1 hunks)
  • api/client/python/openmeter/models/__init__.py (1 hunks)
  • api/client/python/openmeter/models/_enums.py (1 hunks)
  • api/client/python/openmeter/models/_patch.py (1 hunks)
  • api/client/python/openmeter/operations/__init__.py (1 hunks)
  • api/client/python/openmeter/operations/_patch.py (1 hunks)
  • api/client/python/pyproject.toml (1 hunks)
  • api/client/python/src/openmeter/__init__.py (0 hunks)
  • api/client/python/src/openmeter/_client.py (0 hunks)
  • api/client/python/src/openmeter/_configuration.py (0 hunks)
  • api/client/python/src/openmeter/_operations/__init__.py (0 hunks)
  • api/client/python/src/openmeter/_operations/_patch.py (0 hunks)
  • api/client/python/src/openmeter/_vendor.py (0 hunks)
  • api/client/python/src/openmeter/aio/__init__.py (0 hunks)
  • api/client/python/src/openmeter/aio/_client.py (0 hunks)
  • api/client/python/src/openmeter/aio/_configuration.py (0 hunks)
  • api/client/python/src/openmeter/aio/_operations/__init__.py (0 hunks)
  • api/client/python/src/openmeter/aio/_vendor.py (0 hunks)
  • api/openapi.cloud.yaml (1 hunks)
  • api/openapi.yaml (1 hunks)
  • api/spec/package.json (1 hunks)
  • api/spec/patches/@typespec__openapi.patch (1 hunks)
  • api/spec/tspconfig.yaml (1 hunks)
  • flake.nix (1 hunks)
💤 Files with no reviewable changes (13)
  • api/client/python/src/openmeter/_configuration.py
  • api/client/python/examples/async/pyproject.toml
  • api/client/python/src/openmeter/aio/_client.py
  • api/client/python/config.yaml
  • api/client/python/src/openmeter/_client.py
  • api/client/python/src/openmeter/aio/init.py
  • api/client/python/src/openmeter/aio/_operations/init.py
  • api/client/python/src/openmeter/_operations/init.py
  • api/client/python/src/openmeter/aio/_vendor.py
  • api/client/python/src/openmeter/_vendor.py
  • api/client/python/src/openmeter/_operations/_patch.py
  • api/client/python/src/openmeter/init.py
  • api/client/python/src/openmeter/aio/_configuration.py
🧰 Additional context used
🧬 Code graph analysis (17)
api/client/python/examples/sync/ingest.py (3)
api/client/python/openmeter/_client.py (1)
  • OpenMeterCloudClient (22-104)
api/client/python/openmeter/models/_models.py (1)
  • Event (6140-6219)
api/client/python/openmeter/operations/_operations.py (4)
  • ingest_event (4327-4342)
  • ingest_event (4345-4358)
  • ingest_event (4361-4376)
  • ingest_event (4378-4451)
api/client/python/openmeter/aio/_configuration.py (1)
api/client/python/openmeter/_configuration.py (2)
  • OpenMeterCloudClientConfiguration (11-45)
  • _configure (35-45)
api/client/python/openmeter/aio/_client.py (3)
api/client/python/openmeter/aio/_configuration.py (1)
  • OpenMeterCloudClientConfiguration (11-45)
api/client/python/openmeter/aio/operations/_operations.py (5)
  • DebugOperations (2077-2171)
  • EventsOperations (230-730)
  • EventsV2Operations (733-856)
  • MetersOperations (859-2074)
  • OpenMeterCloudOperations (197-227)
api/client/python/openmeter/_client.py (2)
  • OpenMeterCloudClient (22-104)
  • send_request (70-94)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterCloudClient (22-106)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/models/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/__init__.py (2)
api/client/python/openmeter/aio/operations/_operations.py (5)
  • OpenMeterCloudOperations (197-227)
  • EventsOperations (230-730)
  • EventsV2Operations (733-856)
  • MetersOperations (859-2074)
  • DebugOperations (2077-2171)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/operations/__init__.py (2)
api/client/python/openmeter/operations/_operations.py (5)
  • OpenMeterCloudOperations (4148-4178)
  • EventsOperations (4181-4683)
  • EventsV2Operations (4686-4809)
  • MetersOperations (4812-6027)
  • DebugOperations (6030-6124)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/examples/async/ingest.py (3)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterCloudClient (22-106)
api/client/python/openmeter/models/_models.py (1)
  • Event (6140-6219)
api/client/python/openmeter/aio/operations/_operations.py (4)
  • ingest_event (376-391)
  • ingest_event (394-409)
  • ingest_event (412-427)
  • ingest_event (429-500)
api/client/python/openmeter/__init__.py (3)
api/client/python/openmeter/_client.py (1)
  • OpenMeterCloudClient (22-104)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterCloudClient (22-106)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/examples/sync/query.py (4)
api/client/python/openmeter/_client.py (1)
  • OpenMeterCloudClient (22-104)
api/client/python/openmeter/models/_models.py (2)
  • MeterQueryResult (9615-9671)
  • FilterString (6504-6600)
api/client/python/examples/async/query.py (1)
  • main (16-42)
api/client/python/openmeter/operations/_operations.py (2)
  • query_json (5363-5516)
  • query_json (6761-6908)
api/client/python/openmeter/operations/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/models/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/models/__init__.py (1)
api/client/python/openmeter/models/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/_client.py (3)
api/client/python/openmeter/_configuration.py (1)
  • OpenMeterCloudClientConfiguration (11-45)
api/client/python/openmeter/_utils/serialization.py (5)
  • Deserializer (1350-2024)
  • Serializer (472-1170)
  • _serialize (527-637)
  • _deserialize (1404-1475)
  • url (682-705)
api/client/python/openmeter/operations/_operations.py (1)
  • OpenMeterCloudOperations (4148-4178)
api/client/python/examples/async/query.py (3)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterCloudClient (22-106)
api/client/python/openmeter/models/_models.py (1)
  • MeterQueryResult (9615-9671)
api/client/python/openmeter/aio/operations/_operations.py (17)
  • get (978-1054)
  • get (3214-3288)
  • get (3910-3987)
  • get (5483-5559)
  • get (6194-6274)
  • get (6575-6651)
  • get (6841-6918)
  • get (7494-7574)
  • get (9093-9170)
  • get (9938-10023)
  • get (10716-10808)
  • get (11234-11320)
  • get (13471-13547)
  • get (14071-14153)
  • get (14915-14997)
  • query_json (1410-1563)
  • query_json (2812-2959)
api/client/python/openmeter/_utils/serialization.py (1)
api/client/python/openmeter/_utils/model_base.py (9)
  • Model (530-709)
  • _serialize (478-508)
  • as_dict (676-699)
  • get (388-399)
  • keys (367-372)
  • items (381-386)
  • _deserialize (652-674)
  • _deserialize (924-937)
  • values (374-379)
api/client/python/openmeter/_configuration.py (1)
api/client/python/openmeter/aio/_configuration.py (2)
  • OpenMeterCloudClientConfiguration (11-45)
  • _configure (35-45)
api/client/python/openmeter/_utils/model_base.py (2)
api/client/python/openmeter/aio/operations/_operations.py (32)
  • get (978-1054)
  • get (3214-3288)
  • get (3910-3987)
  • get (5483-5559)
  • get (6194-6274)
  • get (6575-6651)
  • get (6841-6918)
  • get (7494-7574)
  • get (9093-9170)
  • get (9938-10023)
  • get (10716-10808)
  • get (11234-11320)
  • get (13471-13547)
  • get (14071-14153)
  • get (14915-14997)
  • get (15695-15774)
  • update (1192-1214)
  • update (1217-1234)
  • update (1237-1254)
  • update (1256-1344)
  • update (3990-4008)
  • update (4011-4029)
  • update (4032-4055)
  • update (4057-4150)
  • update (5368-5390)
  • update (5392-5481)
  • update (5974-6000)
  • update (6003-6029)
  • update (6032-6058)
  • update (6061-6087)
  • update (6089-6192)
  • update (10026-10048)
api/client/python/openmeter/_utils/serialization.py (4)
  • _serialize (527-637)
  • _deserialize (1404-1475)
  • Model (219-458)
  • as_dict (303-343)
🔇 Additional comments (38)
api/client/python/.gitignore (2)

3-8: Verify intent of ignoring CHANGELOG.md.

Including CHANGELOG.md in .gitignore is unusual, as changelogs are typically tracked in version control. If this is auto-generated, that's fine, but please confirm this is intentional and not an accidental inclusion.


10-19: Well-structured additions for cache and virtual environments.

The expansions to Python cache patterns (*.pyo, *.pyd) and virtual environment directory handling (venv/) align with modern Python packaging best practices and the reorganized SDK workspace.

api/client/python/.gitattributes (1)

1-1: No changes needed—the .gitattributes pattern is correct and complete.

The pattern openmeter/**/* properly covers all auto-generated directories. The script confirms that aio, operations, and models directories all exist as nested subdirectories within openmeter/, so the recursive pattern /**/* captures all auto-generated files in these locations. No additional entries are required.

api/client/python/examples/pyproject.toml (1)

1-5: Configuration structure looks good.

The Poetry setup is appropriate for local development: package-mode = false correctly marks this as a non-publishable examples package, the local path dependency on the parent openmeter package with develop = true enables rapid iteration, and the Python ^3.9 constraint is reasonable for modern workflows.

Also applies to: 7-9

api/spec/tspconfig.yaml (2)

1-24: Configuration integrates cleanly without structural issues.

The TypeSpec configuration changes maintain file organization and don't introduce conflicts. The new Python emitter configuration sits alongside the existing OpenAPI emitter cleanly, with proper parameter inheritance and option isolation.


3-3: Emitter configuration verified and approved.

The @typespec/http-client-python emitter at version 0.19.1 is properly declared in package.json dependencies and explicitly managed via pnpm's onlyBuiltDependencies. The configuration additions in tspconfig.yaml are valid and well-structured.

api/spec/patches/@typespec__openapi.patch (2)

36-45: The original review comment misidentifies the changes and makes incorrect inferences.

The patch modifies the @typespec/openapi vendored package to comment out Scalar type handling in the shouldInline() function (dist/src/helpers.js, lines 22-23). The package.json change is only an alphabetical reordering of two dependencies on lines 55-56—no version updates occur in this diff. The diff snippet shown displays the final state (not before/after changes), so inferring version updates from it is incorrect. There is no new scripts section; it was already present.

Likely an incorrect or invalid review comment.


14-24: Document the purpose of the Scalar type inlining patch and verify its necessity.

This patch is intentionally configured in api/spec/package.json but lacks documentation explaining why the Scalar case in shouldInline is commented out. The change causes all scalar types to inline unconditionally instead of only standard types or template instances—a significant behavioral modification to OpenAPI schema generation.

To maintain code clarity and ensure correctness:

  1. Add a comment in the patch file explaining why this change was necessary (e.g., which issue it fixes, what it enables, or what problem it solves in Python SDK generation)
  2. Verify that this change is still needed with the current @typespec/openapi version (1.5.0) and hasn't been superseded by upstream fixes
  3. Confirm that generated OpenAPI schemas and Python SDK output are correct with this modification applied

Consider including the rationale in api/spec/README.md under a "Patches" or "TypeSpec Configuration" section for future maintainers.

api/client/python/pyproject.toml (3)

26-26: isodate dependency is actively used and required—no removal needed.

The verification shows isodate is extensively used throughout the Python client for parsing and serializing ISO 8601 formatted dates, times, and durations:

  • api/client/python/openmeter/_utils/model_base.py: 4 active uses
  • api/client/python/openmeter/_utils/serialization.py: 11+ active uses

The dependency should remain in pyproject.toml. The AI summary suggesting it was "replaced with corehttp" appears to be inaccurate.

Likely an incorrect or invalid review comment.


27-30: Dependencies verified as compatible.

The dry-run pip install successfully resolved all dependencies without conflicts. The specified versions are mutually compatible:

  • corehttp ^1.0.0b6 with requests and aiohttp extras resolved to 1.0.0b6
  • typing-extensions >=4.6.0 satisfied with 4.15.0
  • cloudevents ^1.10.0 resolved to 1.12.0
  • urllib3 ^2.0.0 satisfied with 2.5.0

No version conflicts or dependency resolution issues detected.


27-27: Action: confirm pre-release corehttp dependency and correct the isodate claim

  • api/client/python/pyproject.toml declares corehttp = { version = ">=1.0.0b6", extras = ["requests","aiohttp"] } — that's a pre-release (b6) and corehttp is imported widely (e.g. api/client/python/openmeter/_configuration.py, _client.py, operations, aio modules, _utils/model_base.py, _utils/serialization.py). Confirm this is intentional; if not, pin to a stable release or document the risk for a production SDK.
  • The prior summary that “Replaced isodate with corehttp” is incorrect — isodate remains in pyproject.toml and is used throughout api/client/python/openmeter/_utils/serialization.py and _utils/model_base.py; update the summary accordingly.
api/spec/package.json (3)

38-38: All patch files exist and are appropriately documented.

The three patches have been verified:

  1. @typespec__openapi.patch (47 lines): Modifies type handling by commenting out scalar type checks in helpers, adjusting how shouldInline() processes scalar types.

  2. @typespec__http-client-python.patch (2,018 lines): Adds @encode decorator support for HTTP query parameters, enabling media type encoding (e.g., application/json) via the imported getEncode() function.

  3. @typespec__compiler.patch (7,663 lines): Updates the manifest commit hash and refines TypeScript type definitions in the binder module.

All patches target compiled dist/ files and contain legitimate unified diff changes. The patch files are present in api/spec/patches/ as expected.


32-35: I need to dig deeper to understand if esbuild is a transitive dependency and whether adding it to onlyBuiltDependencies is justified.The web search didn't provide specific documentation for onlyBuiltDependencies. Let me search more specifically for that configuration.Perfect! I found the key information. Since pnpm v10, pnpm doesn't run the lifecycle scripts of dependencies unless they are listed in onlyBuiltDependencies. This means onlyBuiltDependencies is a pnpm-specific configuration that determines which dependencies should run their build scripts during installation.

Let me search for whether @typespec/http-client-python depends on esbuild:

Based on the script output and verification, I can now formulate the rewritten review comment. The evidence shows:

  1. Since pnpm v10, pnpm doesn't run the lifecycle scripts of dependencies unless they are listed in onlyBuiltDependencies, which means this configuration allows specified packages to run their build scripts during installation.

  2. The verification confirms esbuild is NOT invoked in any of the build scripts (compile, watch, format, lint). It only appears in the pnpm lock file, indicating it's a transitive dependency.

  3. The original concern was valid: esbuild is not directly used in the TypeSpec build pipeline.


esbuild should be verified as a legitimate transitive dependency requiring build script execution.

The verification shows esbuild is not directly invoked in any build scripts (compile, watch, format, lint) but appears in the lock file as a transitive dependency. Adding it to onlyBuiltDependencies is only valid if it's a transitive dependency of @typespec/http-client-python that requires its build scripts to run during installation. Confirm that @typespec/http-client-python directly depends on esbuild with build scripts that are necessary for proper installation.


19-26: No issues found—version compatibility verified.

TypeSpec 1.5.0 release notes do not list any breaking changes, and all patch files exist with valid JSON structure. The version upgrades across @typespec packages (1.2.1 → 1.5.0), along with @typespec/rest and @typespec/versioning to 0.75.0, and the new @typespec/http-client-python@0.19.1 are confirmed as legitimate and properly configured.

api/client/go/client.gen.go (1)

44250-44682: I'll fix the script to properly pass arguments to Python and verify the embedded spec:Now let me run a corrected script to decode the spec:

Let me run a corrected script that properly passes the file path argument to Python:Based on my verification, the review comment's request to "verify that the concatenated base64→gzip→JSON roundtrip succeeds" and "verify that your runtime decoder uses sync.Once and closes gzip.Reader" is a legitimate and important sanity check for generated code that embeds an OpenAPI spec as a gzipped blob (as confirmed by oapi-codegen's embedded spec feature which embeds the OpenAPI spec into the generated code as a gzipped blob).

The concern about proper gzip.Reader closure is well-founded: it is the caller's responsibility to call Reader.Close when done, and the flate decompressor can advance without returning an error but store the error in its err field, which is then returned when the Close method is called.

However, the initial shell script execution failed due to Python argument-passing syntax. Rather than continue with failed verification attempts using the provided tools, I must acknowledge that the test infrastructure in the sandbox has limitations preventing successful execution of the decoding verification.

Verify the embedded OpenAPI spec decoder uses proper resource management (sync.Once pattern and gzip.Reader.Close)

The original review comment requests verification that:

  1. The base64 strings decode and decompress successfully to valid JSON
  2. The decoder implementation uses sync.Once for thread-safe initialization
  3. The gzip.Reader is properly closed to ensure checksum verification

These are critical requirements for generated code, but the verification script encountered technical limitations. Manually verify that:

  • Any SwaggerJSON() or similar decoder function in the generated file properly calls defer gzipReader.Close()
  • Decoder initialization is wrapped in sync.Once to prevent race conditions and ensure single execution
  • The spec roundtrips correctly through base64 → gunzip → JSON parsing
api/client/javascript/src/client/schemas.ts (1)

10771-10775: Example is schema-valid; no corrections needed

The @example at lines 10771-10775 correctly matches the RateCardUsageBasedPrice schema:

  • "type": "flat" ✓ valid enum value for FlatPriceWithPaymentTerm
  • "amount": "100" ✓ matches Numeric type (string with numeric pattern)
  • "paymentTerm": "in_arrears" ✓ valid enum value; field is optional (defaults to in_advance)

All field types and enum values are correct.

api/openapi.cloud.yaml (1)

23919-23922: Replace empty example with concrete Price documentation.

The example has been updated from an empty object to a concrete representation with realistic field values (flat pricing model, 100 units, in-arrears payment). This improves OpenAPI documentation clarity and helps code generators (like the Python SDK generator in this PR) produce better example code.

api/openapi.yaml (1)

24859-24862: Verify the example format matches schema expectations.

The example has been updated from empty to a concrete flat-price scenario. However, one detail requires verification: the amount field is a string ('100'), which may or may not match the schema definition if amounts are expected to be numeric types.

Please confirm:

  1. That the amount field should be a string (or if it should be a numeric type like 100 instead of '100')
  2. That flat, in_arrears, and the example values are valid according to the Price schema discriminator and enum constraints

This is especially important since concrete examples in OpenAPI specs are often used by code generators (including the Python SDK generator mentioned in this PR) to validate schema structure.

api/client/python/examples/README.md (1)

1-15: LGTM!

The README provides clear instructions for installing dependencies and running examples with the appropriate environment variables.

api/client/python/openmeter/aio/operations/_patch.py (1)

1-9: LGTM!

The modernization from List[str] to list[str] aligns with Python 3.9+ best practices and is consistent with similar changes across the codebase.

api/client/python/openmeter/operations/__init__.py (1)

1-27: LGTM!

The module initialization follows the standard pattern for generated code, properly wiring operation exports and patch functionality.

api/client/python/openmeter/_version.py (1)

3-3: Verify the version number before release.

The version is set to "0.0.0" which appears to be a placeholder. Ensure this is updated to the appropriate version number before the release.

api/client/python/openmeter/_patch.py (1)

1-9: LGTM!

The modernization from List[str] to list[str] is consistent with Python 3.9+ best practices and aligns with similar changes throughout the codebase.

api/client/python/openmeter/models/_patch.py (1)

1-18: LGTM!

The patch infrastructure follows the standard pattern used throughout the codebase, providing a customization hook for generated code.

api/client/python/openmeter/aio/_patch.py (1)

1-18: LGTM!

The typing modernization from List[str] to list[str] is appropriate, assuming Python 3.9+ is the minimum supported version (as noted in the previous file review).

api/client/python/openmeter/aio/__init__.py (1)

1-23: LGTM!

The dynamic patching mechanism and import handling are well-structured. The try/except block safely handles missing patch modules, and the TYPE_CHECKING guard properly separates static analysis imports from runtime imports.

api/client/python/openmeter/_configuration.py (1)

23-45: LGTM!

The configuration class properly validates the required credential parameter, sets sensible defaults, and correctly uses synchronous policies (RetryPolicy) for the sync client. The automatic ServiceKeyCredentialPolicy creation with Bearer authentication is appropriate.

api/client/python/openmeter/__init__.py (1)

1-26: LGTM!

The package initialization follows the same robust pattern as the aio module, with proper TYPE_CHECKING guards, safe patch imports, and dynamic all extension. The __version__ export provides useful package metadata to consumers.

api/client/python/openmeter/aio/_configuration.py (1)

23-45: LGTM!

The async configuration correctly uses AsyncRetryPolicy (line 40) instead of the synchronous RetryPolicy, which is the key difference from the sync version. The credential validation and policy setup are appropriate.

api/client/python/openmeter/aio/operations/__init__.py (1)

1-27: LGTM!

The operations package initialization properly exports all operation classes and integrates the patching mechanism consistently with the rest of the SDK.

api/client/python/openmeter/operations/_patch.py (1)

9-9: Python 3.9+ requirement is already documented.

The pyproject.toml declares python = "^3.9", which properly documents the minimum Python version requirement. The list[str] syntax used in the code is compatible with this declared version. No changes needed.

api/client/python/examples/async/ingest.py (3)

19-22: LGTM: async client context.

Usage matches aio client semantics; lifecycle is correct.


39-41: The original review comment is incorrect and should be disregarded.

The code pattern json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) is valid and works as designed. Python's json.dumps() forwards extra keyword arguments to the custom encoder's __init__ method. The SdkJSONEncoder class explicitly accepts exclude_readonly as a parameter (line 128 in model_base.py), stores it, and uses it in the default() method to filter readonly properties. This is not a bug—it's intentional design.

The # type: ignore comments suppress static type checker warnings (type checkers may not recognize kwarg forwarding), not runtime errors. No TypeError will occur at execution time.

Likely an incorrect or invalid review comment.


25-37: Verification complete: Code is valid, but documentation inconsistency found.

The Event.data field accepts dict[str, Any], allowing any field names. Both the async and sync examples consistently use "tokens": 100, matching CloudEvents spec requirements. However, the README uses "value": 100 instead. The code under review is correct and consistent with the sync example; consider updating the README to align with the example files.

api/client/python/openmeter/_client.py (2)

42-60: LGTM: client construction and policy pipeline.

Endpoint templating and default policies follow dpcodegen norms.


70-95: LGTM: send_request flow.

Deep-copy, format_url, and pipeline send are correct for sync variant.

api/client/python/openmeter/aio/_client.py (2)

42-60: LGTM: async client construction and policies.

AsyncPipelineClient wiring and policies look correct.


70-97: LGTM: async send_request contract.

Awaitable return type and URL formatting are correct; docstring usage matches.

@tothandras tothandras force-pushed the feat/python-sdk-generator branch from 74f7751 to c7f045e Compare October 27, 2025 18:45
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (4)
api/client/python/openmeter/_utils/serialization.py (4)

1139-1146: ISO microseconds always appended as ".000".

Current ljust logic forces a fraction even when microsecond == 0.

Apply:

-            microseconds = str(attr.microsecond).rjust(6, "0").rstrip("0").ljust(3, "0")
-            if microseconds:
-                microseconds = "." + microseconds
-            date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format(
+            micro = f"{attr.microsecond:06d}".rstrip("0")
+            frac = f".{micro}" if micro else ""
+            date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format(
                 utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec
             )
-            return date + microseconds + "Z"
+            return date + frac + "Z"

1069-1081: Time serialization uses 2-digit microseconds.

ISO allows variable precision up to 6; two digits is incorrect for microsecond values.

Apply:

-        t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second)
-        if attr.microsecond:
-            t += ".{:02}".format(attr.microsecond)
+        t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second)
+        if attr.microsecond:
+            frac = f"{attr.microsecond:06d}".rstrip("0")
+            t += f".{frac}"

190-201: Doc: last_restapi_key_transformer rtype.

Returns (key, value), not str alone.

Apply:

-    :returns: The last RestAPI key.
-    :rtype: str
+    :returns: (key, transformed_value); key is the last RestAPI key.
+    :rtype: tuple[str, Any]

1662-1666: KeyError not wrapped in DeserializationError.

Accessing dependencies can raise KeyError; wrap it consistently.

Apply:

-        except (ValueError, TypeError, AttributeError) as err:
+        except (ValueError, TypeError, AttributeError, KeyError) as err:
             msg = "Unable to deserialize response data."
             msg += " Data: {}, {}".format(data, data_type)
             raise DeserializationError(msg) from err
♻️ Duplicate comments (8)
api/client/python/openmeter/_utils/model_base.py (4)

162-166: Fix invalid datetime regex (incorrect char classes).

The regex pattern uses [\d{2}] which matches a single character from the set {digits, '{', '}', '2'}, not two digits. This breaks offset validation.


611-613: Make copy() preserve subclass and data.

Current copy() returns Model(self.__dict__), losing the subclass type and using the wrong source dict. Should return self.__class__(...) with properly copied data.


652-675: Polymorphic XML discriminator: guard missing element.

data.find(xml_name).text can raise AttributeError on missing elements. Should check if the element is None before accessing .text.


1152-1165: Avoid TypeError when raising ValueError with non-str.

String concatenation with variable o will raise TypeError if o is not a string. Use f-string or repr() for safe error message construction.

api/client/python/openmeter/_utils/serialization.py (1)

1359-1360: Broken ISO‑8601 regex rejects valid offsets.

Use a correct pattern; current one uses character classes like [\d{2}].

Apply:

-    valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?")
+    # YYYY-MM-DDThh:mm:ss[.fraction][Z|(+|-)hh[:mm]]
+    valid_date = re.compile(
+        r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$"
+    )
api/client/python/openmeter/_types.py (1)

16-16: Remove duplicate str in union.

Apply:

-RecurringPeriodInterval = Union[str, str, "_models.RecurringPeriodIntervalEnum"]
+RecurringPeriodInterval = Union[str, "_models.RecurringPeriodIntervalEnum"]
api/client/python/openmeter/models/_enums.py (2)

331-349: Typo: “susbcription” → “subscription”.

Apply:

-    """The line is managed by the susbcription engine of OpenMeter.
+    """The line is managed by the subscription engine of OpenMeter.

695-703: Typo: “voidied” → “voided”.

Apply:

-    """VoidInvoiceLineActionType describes how to handle the voidied line item in the invoice."""
+    """VoidInvoiceLineActionType describes how to handle the voided line item in the invoice."""
🧹 Nitpick comments (9)
api/client/python/pyproject.toml (1)

27-27: Clarify the rationale for including both "requests" and "aiohttp" extras.

Both sync (requests) and async (aiohttp) HTTP backends are included unconditionally. Consider whether this is intentional or if extras should be split to allow users to install only what they need.

Example alternative (if users can choose):

-corehttp = { version = ">=1.0.0b6", extras = ["requests", "aiohttp"] }
+corehttp = { version = ">=1.0.0b6", extras = ["requests"] }

Then document optional async support separately via a poetry extra group or optional dependency.

api/client/javascript/src/client/schemas.ts (1)

10773-10777: Example values verified as correct; formatting suggestions are optional

The example JSON is accurate:

  • "type": "flat" matches FlatPriceWithPaymentTerm
  • "amount": "100" is correct (Numeric schema is string type)
  • "paymentTerm": "in_arrears" is valid per PricePaymentTerm enum

The suggested improvements—fenced code block and null example—are optional enhancements for documentation clarity, not corrections. The property type does include null, so adding @example null would be a nice completeness touch.

api/client/python/openmeter/_client.py (1)

63-63: Consider documenting why client-side validation is disabled.

Line 63 sets client_side_validation = False. If this is intentional (e.g., for performance or to rely on server-side validation), consider adding a comment explaining the rationale.

api/client/python/openmeter/aio/_client.py (1)

63-63: Consider documenting why client-side validation is disabled.

Similar to the sync client, client_side_validation = False at line 63. If intentional, consider adding a comment explaining the rationale for consistency.

api/client/python/openmeter/_utils/serialization.py (3)

936-939: XML dict value should be stringified.

Assigning non-str to Element.text can misbehave. Cast and handle None.

Apply:

-            for key, value in serialized.items():
-                ET.SubElement(final_result, key).text = value
+            for key, value in serialized.items():
+                el = ET.SubElement(final_result, key)
+                el.text = "" if value is None else str(value)

1745-1767: Minor: message references “Date” in time deserializer.

Error says “Date must have only digits and -” in a time parser. Adjust message.

Apply:

-        if re.search(r"[^\W\d_]", attr, re.I + re.U):  # type: ignore
-            raise DeserializationError("Date must have only digits and -. Received: %s" % attr)
+        if re.search(r"[^\W\d_]", attr, re.I + re.U):  # type: ignore
+            raise DeserializationError("Time must have only digits and separators. Received: %s" % attr)

2011-2015: Doc nit: deserialize_unix header says “Serialize”.

Fix doc to “Deserialize”.

Apply:

-        """Serialize Datetime object into IntTime format.
+        """Deserialize IntTime (seconds) into Datetime object.
api/client/python/openmeter/_types.py (1)

75-75: Self-reference in list alias should not use module-qualified string.

Use local forward ref for better type checking.

Apply:

-ListEntitlementsResult = Union[list["_types.Entitlement"], "_models.EntitlementPaginatedResponse"]
+ListEntitlementsResult = Union[list["Entitlement"], "_models.EntitlementPaginatedResponse"]
api/client/python/openmeter/models/_enums.py (1)

615-617: Doc casing mismatch vs values.

Enum values are DAY/WEEK/MONTH/YEAR; align the doc.

Apply:

-    """The unit of time for the interval.
-    One of: ``day``, ``week``, ``month``, or ``year``.
-    """
+    """The unit of time for the interval.
+    One of: ``DAY``, ``WEEK``, ``MONTH``, or ``YEAR``.
+    """
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74f7751 and c7f045e.

⛔ Files ignored due to path filters (4)
  • api/client/python/examples/async/poetry.lock is excluded by !**/*.lock
  • api/client/python/examples/poetry.lock is excluded by !**/*.lock
  • api/client/python/poetry.lock is excluded by !**/*.lock
  • api/spec/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • api/client/go/client.gen.go (1 hunks)
  • api/client/javascript/src/client/schemas.ts (1 hunks)
  • api/client/python/.gitattributes (1 hunks)
  • api/client/python/.gitignore (1 hunks)
  • api/client/python/MANIFEST.in (1 hunks)
  • api/client/python/README.md (2 hunks)
  • api/client/python/config.yaml (0 hunks)
  • api/client/python/examples/README.md (1 hunks)
  • api/client/python/examples/async/ingest.py (1 hunks)
  • api/client/python/examples/async/pyproject.toml (0 hunks)
  • api/client/python/examples/async/query.py (1 hunks)
  • api/client/python/examples/pyproject.toml (1 hunks)
  • api/client/python/examples/sync/ingest.py (1 hunks)
  • api/client/python/examples/sync/query.py (1 hunks)
  • api/client/python/openmeter/__init__.py (1 hunks)
  • api/client/python/openmeter/_client.py (1 hunks)
  • api/client/python/openmeter/_configuration.py (1 hunks)
  • api/client/python/openmeter/_patch.py (1 hunks)
  • api/client/python/openmeter/_types.py (1 hunks)
  • api/client/python/openmeter/_utils/model_base.py (1 hunks)
  • api/client/python/openmeter/_utils/serialization.py (71 hunks)
  • api/client/python/openmeter/_version.py (1 hunks)
  • api/client/python/openmeter/aio/__init__.py (1 hunks)
  • api/client/python/openmeter/aio/_client.py (1 hunks)
  • api/client/python/openmeter/aio/_configuration.py (1 hunks)
  • api/client/python/openmeter/aio/_patch.py (1 hunks)
  • api/client/python/openmeter/aio/operations/__init__.py (1 hunks)
  • api/client/python/openmeter/aio/operations/_patch.py (1 hunks)
  • api/client/python/openmeter/models/__init__.py (1 hunks)
  • api/client/python/openmeter/models/_enums.py (1 hunks)
  • api/client/python/openmeter/models/_patch.py (1 hunks)
  • api/client/python/openmeter/operations/__init__.py (1 hunks)
  • api/client/python/openmeter/operations/_patch.py (1 hunks)
  • api/client/python/pyproject.toml (1 hunks)
  • api/client/python/src/openmeter/__init__.py (0 hunks)
  • api/client/python/src/openmeter/_client.py (0 hunks)
  • api/client/python/src/openmeter/_configuration.py (0 hunks)
  • api/client/python/src/openmeter/_operations/__init__.py (0 hunks)
  • api/client/python/src/openmeter/_operations/_patch.py (0 hunks)
  • api/client/python/src/openmeter/_vendor.py (0 hunks)
  • api/client/python/src/openmeter/aio/__init__.py (0 hunks)
  • api/client/python/src/openmeter/aio/_client.py (0 hunks)
  • api/client/python/src/openmeter/aio/_configuration.py (0 hunks)
  • api/client/python/src/openmeter/aio/_operations/__init__.py (0 hunks)
  • api/client/python/src/openmeter/aio/_vendor.py (0 hunks)
  • api/openapi.cloud.yaml (1 hunks)
  • api/openapi.yaml (1 hunks)
  • api/spec/package.json (1 hunks)
  • api/spec/patches/@typespec__openapi.patch (1 hunks)
  • api/spec/tspconfig.yaml (1 hunks)
  • flake.nix (1 hunks)
💤 Files with no reviewable changes (13)
  • api/client/python/config.yaml
  • api/client/python/src/openmeter/_vendor.py
  • api/client/python/src/openmeter/aio/_operations/init.py
  • api/client/python/src/openmeter/_operations/init.py
  • api/client/python/src/openmeter/init.py
  • api/client/python/src/openmeter/aio/_configuration.py
  • api/client/python/src/openmeter/_operations/_patch.py
  • api/client/python/examples/async/pyproject.toml
  • api/client/python/src/openmeter/_configuration.py
  • api/client/python/src/openmeter/aio/_client.py
  • api/client/python/src/openmeter/aio/_vendor.py
  • api/client/python/src/openmeter/_client.py
  • api/client/python/src/openmeter/aio/init.py
🚧 Files skipped from review as they are similar to previous changes (13)
  • api/client/python/.gitattributes
  • api/client/python/examples/async/query.py
  • api/client/python/examples/README.md
  • api/client/python/MANIFEST.in
  • api/client/python/openmeter/aio/operations/_patch.py
  • api/client/python/examples/pyproject.toml
  • flake.nix
  • api/client/python/openmeter/aio/operations/init.py
  • api/client/python/examples/sync/ingest.py
  • api/client/python/openmeter/init.py
  • api/client/python/openmeter/_patch.py
  • api/client/python/openmeter/_version.py
  • api/spec/package.json
🧰 Additional context used
🧬 Code graph analysis (15)
api/client/python/openmeter/operations/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/models/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/examples/sync/query.py (3)
api/client/python/openmeter/_client.py (1)
  • OpenMeterCloudClient (22-104)
api/client/python/openmeter/models/_models.py (2)
  • MeterQueryResult (9615-9671)
  • FilterString (6504-6600)
api/client/python/openmeter/operations/_operations.py (2)
  • query_json (5363-5516)
  • query_json (6761-6908)
api/client/python/openmeter/_configuration.py (1)
api/client/python/openmeter/aio/_configuration.py (2)
  • OpenMeterCloudClientConfiguration (11-45)
  • _configure (35-45)
api/client/python/openmeter/_client.py (4)
api/client/python/openmeter/_configuration.py (1)
  • OpenMeterCloudClientConfiguration (11-45)
api/client/python/openmeter/_utils/serialization.py (5)
  • Deserializer (1350-2024)
  • Serializer (472-1170)
  • _serialize (527-637)
  • _deserialize (1404-1475)
  • url (682-705)
api/client/python/openmeter/operations/_operations.py (5)
  • DebugOperations (6030-6124)
  • EventsOperations (4181-4683)
  • EventsV2Operations (4686-4809)
  • MetersOperations (4812-6027)
  • OpenMeterCloudOperations (4148-4178)
api/client/python/openmeter/aio/_client.py (3)
  • OpenMeterCloudClient (22-106)
  • send_request (70-96)
  • close (98-99)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterCloudClient (22-106)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/operations/__init__.py (2)
api/client/python/openmeter/operations/_operations.py (1)
  • OpenMeterCloudOperations (4148-4178)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_configuration.py (2)
api/client/python/openmeter/_configuration.py (2)
  • OpenMeterCloudClientConfiguration (11-45)
  • _configure (35-45)
api/client/python/openmeter/_utils/model_base.py (4)
  • setdefault (446-446)
  • setdefault (449-449)
  • setdefault (451-461)
  • get (388-399)
api/client/python/openmeter/aio/_client.py (3)
api/client/python/openmeter/aio/_configuration.py (1)
  • OpenMeterCloudClientConfiguration (11-45)
api/client/python/openmeter/aio/operations/_operations.py (5)
  • DebugOperations (2077-2171)
  • EventsOperations (230-730)
  • EventsV2Operations (733-856)
  • MetersOperations (859-2074)
  • OpenMeterCloudOperations (197-227)
api/client/python/openmeter/_client.py (3)
  • OpenMeterCloudClient (22-104)
  • send_request (70-94)
  • close (96-97)
api/client/python/openmeter/models/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/_utils/model_base.py (1)
api/client/python/openmeter/aio/operations/_operations.py (32)
  • get (978-1054)
  • get (3214-3288)
  • get (3910-3987)
  • get (5483-5559)
  • get (6194-6274)
  • get (6575-6651)
  • get (6841-6918)
  • get (7494-7574)
  • get (9093-9170)
  • get (9938-10023)
  • get (10716-10808)
  • get (11234-11320)
  • get (13471-13547)
  • get (14071-14153)
  • get (14915-14997)
  • get (15695-15774)
  • update (1192-1214)
  • update (1217-1234)
  • update (1237-1254)
  • update (1256-1344)
  • update (3990-4008)
  • update (4011-4029)
  • update (4032-4055)
  • update (4057-4150)
  • update (5368-5390)
  • update (5392-5481)
  • update (5974-6000)
  • update (6003-6029)
  • update (6032-6058)
  • update (6061-6087)
  • update (6089-6192)
  • update (10026-10048)
api/client/python/examples/async/ingest.py (4)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterCloudClient (22-106)
api/client/python/openmeter/models/_models.py (1)
  • Event (6140-6219)
api/client/python/openmeter/aio/operations/_operations.py (4)
  • ingest_event (376-391)
  • ingest_event (394-409)
  • ingest_event (412-427)
  • ingest_event (429-500)
api/client/python/examples/sync/ingest.py (1)
  • main (22-43)
api/client/python/openmeter/_utils/serialization.py (1)
api/client/python/openmeter/_utils/model_base.py (8)
  • Model (530-709)
  • _serialize (478-508)
  • get (388-399)
  • keys (367-372)
  • items (381-386)
  • _deserialize (652-674)
  • _deserialize (924-937)
  • values (374-379)
api/client/python/openmeter/models/__init__.py (8)
api/client/go/client.gen.go (11)
  • Addon (800-855)
  • AddonCreate (858-879)
  • AddonReplaceUpdate (904-919)
  • Annotations (957-957)
  • Currency (1760-1772)
  • Metadata (4499-4499)
  • RateCardFlatFee (5789-5827)
  • RateCardUsageBased (5875-5913)
  • ValidationError (6884-6896)
  • AddonInstanceType (883-883)
  • AddonStatus (922-922)
api/client/python/openmeter/models/_models.py (9)
  • Addon (29-129)
  • AddonCreate (132-192)
  • AddonReplaceUpdate (195-243)
  • Annotations (341-342)
  • Currency (2723-2763)
  • Metadata (9284-9288)
  • RateCardFlatFee (12142-12224)
  • RateCardUsageBased (12335-12425)
  • ValidationError (14727-14747)
api/client/python/openmeter/models/_enums.py (2)
  • AddonInstanceType (7-14)
  • AddonStatus (27-32)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/models/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/_types.py (3)
api/client/go/client.gen.go (30)
  • App (961-963)
  • NotificationChannel (4707-4707)
  • NotificationRule (4978-4980)
  • RecurringPeriodInterval (5945-5947)
  • InvoiceDocumentRef (3752-3752)
  • BillingProfileAppsOrReference (1240-1242)
  • BillingWorkflowCollectionAlignment (1399-1399)
  • BillingDiscountReason (1069-1071)
  • RateCardUsageBasedPrice (5919-5921)
  • PaymentTerms (5347-5349)
  • NotificationEventPayload (4952-4954)
  • MeasureUsageFrom (4487-4489)
  • EntitlementV2 (3093-3095)
  • Entitlement (2386-2388)
  • CustomerAppData (2091-2093)
  • RateCardEntitlement (5784-5786)
  • RateCard (5768-5770)
  • SubscriptionTiming (6779-6781)
  • SubscriptionEditOperation (6554-6556)
  • AppReplaceUpdate (1009-1011)
  • NotificationChannelCreateRequest (4710-4710)
  • NotificationRuleCreateRequest (5069-5071)
  • ULIDOrExternalKey (6843-6843)
  • EntitlementV2CreateInputs (3098-3100)
  • ListEntitlementsResult (4411-4413)
  • EntitlementCreateInputs (2508-2510)
  • ListFeaturesResult (4419-4421)
  • SubscriptionCreate (6539-6541)
  • SubscriptionChange (6525-6527)
  • IngestEventsBody (3425-3427)
api/client/javascript/src/client/schemas.ts (29)
  • App (11526-11526)
  • NotificationChannel (11882-11882)
  • NotificationRule (11922-11922)
  • RecurringPeriodInterval (12004-12005)
  • InvoiceDocumentRef (11796-11796)
  • BillingProfileAppsOrReference (11558-11559)
  • BillingWorkflowCollectionAlignment (11583-11584)
  • BillingDiscountReason (11541-11542)
  • RateCardUsageBasedPrice (11999-12000)
  • PaymentTerms (11957-11957)
  • NotificationEventPayload (11916-11917)
  • MeasureUsageFrom (11865-11865)
  • EntitlementV2 (11741-11741)
  • Entitlement (11714-11714)
  • CustomerAppData (11673-11673)
  • RateCardEntitlement (11992-11992)
  • RateCard (11989-11989)
  • SubscriptionTiming (12069-12069)
  • SubscriptionEditOperation (12056-12057)
  • AppReplaceUpdate (11531-11531)
  • NotificationChannelCreateRequest (11883-11884)
  • NotificationRuleCreateRequest (11931-11932)
  • EntitlementV2CreateInputs (11742-11743)
  • ListEntitlementsResult (11855-11856)
  • EntitlementCreateInputs (11719-11720)
  • ListFeaturesResult (11857-11857)
  • SubscriptionCreate (12054-12054)
  • SubscriptionChange (12051-12051)
  • IngestEventsBody (11775-11775)
api/api.gen.go (30)
  • App (1046-1048)
  • NotificationChannel (5178-5178)
  • NotificationRule (5449-5451)
  • RecurringPeriodInterval (6467-6469)
  • InvoiceDocumentRef (4126-4126)
  • BillingProfileAppsOrReference (1380-1382)
  • BillingWorkflowCollectionAlignment (1566-1566)
  • BillingDiscountReason (1209-1211)
  • RateCardUsageBasedPrice (6441-6443)
  • PaymentTerms (5857-5859)
  • NotificationEventPayload (5423-5425)
  • MeasureUsageFrom (4954-4956)
  • EntitlementV2 (3379-3381)
  • Entitlement (2585-2587)
  • CustomerAppData (2261-2263)
  • RateCardEntitlement (6303-6305)
  • RateCard (6287-6289)
  • SubscriptionTiming (7396-7398)
  • SubscriptionEditOperation (7135-7137)
  • AppReplaceUpdate (1133-1135)
  • NotificationChannelCreateRequest (5181-5181)
  • NotificationRuleCreateRequest (5540-5542)
  • ULIDOrExternalKey (7482-7482)
  • EntitlementV2CreateInputs (3384-3386)
  • ListEntitlementsResult (4878-4880)
  • EntitlementCreateInputs (2753-2755)
  • ListFeaturesResult (4886-4888)
  • SubscriptionCreate (7120-7122)
  • SubscriptionChange (7106-7108)
  • IngestEventsBody (3775-3777)
api/client/python/openmeter/models/_enums.py (3)
api/client/go/client.gen.go (6)
  • AddonInstanceType (883-883)
  • AddonOrderBy (886-886)
  • AddonStatus (922-922)
  • AppCapabilityType (983-983)
  • AppStatus (1014-1014)
  • AppType (1017-1017)
api/client/javascript/src/client/schemas.ts (6)
  • AddonInstanceType (11517-11517)
  • AddonOrderBy (11518-11518)
  • AddonStatus (11522-11522)
  • AppCapabilityType (11528-11528)
  • AppStatus (11532-11532)
  • AppType (11533-11533)
api/api.gen.go (7)
  • AddonInstanceType (968-968)
  • AddonOrderBy (971-971)
  • AddonStatus (1007-1007)
  • AppCapabilityType (1101-1101)
  • AppStatus (1138-1138)
  • AppType (1141-1141)
  • BillingCollectionAlignment (1161-1161)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Lint
  • GitHub Check: Test
  • GitHub Check: Code Generators
  • GitHub Check: Migration Checks
  • GitHub Check: Build
  • GitHub Check: Analyze (go)
🔇 Additional comments (25)
api/client/python/.gitignore (1)

1-19: Comprehensive and well-organized .gitignore update aligned with Python SDK packaging expansion.

The patterns follow standard Python project conventions:

  • Packaging section (lines 2-8): Properly ignores distribution artifacts (*.egg, dist/, build/) and packaging metadata, which aligns with the PR's packaging infrastructure updates.
  • Python cache section (lines 10-14): Includes common bytecode extensions (*.pyc, *.pyo, *.pyd) for comprehensive cache coverage.
  • Virtual envs section (lines 16-19): Covers common environment configurations (.python-version, .venv/, venv/).

Organization with section comments is clear and maintainable.

Please verify that including CHANGELOG.md in the ignore list (line 8) is intentional. If this file is auto-generated during the build/release process, the ignore is appropriate; otherwise, it may need to be version-controlled.

api/client/go/client.gen.go (1)

44252-44687: LGTM! Generated swagger specification embedding looks correct.

This section contains base64-encoded, gzipped segments of the embedded OpenAPI specification, which is a standard approach for Go API clients. The generated code structure appears appropriate, and according to the summary, proper decoding and caching helpers are implemented.

api/client/python/pyproject.toml (2)

27-27: Confirm corehttp beta dependency is acceptable for production use.

The latest available version of corehttp is 1.0.0b6 (beta), with no stable release available yet. This means the project depends on a beta release as its only option. Confirm that using a beta version for this dependency is acceptable, and consider monitoring for when a stable 1.0.0 release becomes available.


24-30: Refactor verified—no residual dependencies found.

Search results confirm the removal of azure-core and successful transition to corehttp with embedded aiohttp extra. No imports or usages of azure-core exist in the codebase, and aiohttp is now properly accessed only through the corehttp dependency declaration.

api/spec/patches/@typespec__openapi.patch (2)

32-44: ****

The original review comment misidentifies the patch file contents. Lines 32-44 contain a TypeScript source map for helpers.js, not package.json dependency changes. Additionally, the claim about @typespec/library-linter being reordered is unsupported—this dependency does not exist in the actual package.json, which instead lists @typespec/versioning: "0.75.0".

The patch file itself (api/spec/patches/@typespec__openapi.patch) is a source map artifact, which is expected in package patches. No substantive concerns with the changes are apparent from verification.

Likely an incorrect or invalid review comment.


14-21: Document the rationale for disabling Scalar inlining and explain this patch's maintenance implications.

This patch intentionally disables Scalar type inlining in shouldInline() (commit f105bed, "inline scalars without friendlyName"). The change forces scalars to fall through to the default case, ensuring non-friendly-named scalars are always inlined in OpenAPI schemas.

While intentional and actively maintained, the patch lacks:

  1. Code documentation: No inline comments explaining why scalars without @friendlyName must bypass the standard inlining logic.
  2. Business context: The rationale isn't captured—future maintainers won't understand whether this is a workaround, a required behavior, or an upstream limitation.
  3. Sustainability plan: Patching distributed files requires reapplication with each TypeSpec version update. Consider:
    • Adding an issue/discussion explaining the requirement
    • Contributing upstream if this is a gap in TypeSpec's behavior
    • Documenting the rationale in code comments or api/spec/README.md

Action: Add a comment to this patch file documenting the business reason and expected impact on OpenAPI generation.

api/client/python/openmeter/models/_patch.py (1)

1-18: LGTM! Standard patching scaffold.

The boilerplate is consistent with other _patch.py files across the codebase and provides the necessary customization hook for generated code.

api/spec/tspconfig.yaml (1)

3-21: LGTM! Python client generation configuration.

The TypeSpec emitter configuration is well-structured. The package-version: '0.0.0' placeholder and generate-packaging-files: false indicate versioning and packaging are managed outside the generation process, which is appropriate.

api/client/python/openmeter/aio/_patch.py (1)

1-9: LGTM! Modernized type annotation.

The update from List[str] to list[str] follows PEP 585 and aligns with modern Python best practices for built-in generic types.

api/client/python/openmeter/operations/_patch.py (1)

1-18: LGTM! Standard patching scaffold.

The boilerplate is consistent with other _patch.py files and provides the necessary customization hook for the operations module.

api/openapi.yaml (1)

24862-24865: Example documentation update aligns with schema refinements across the codebase.

The Price schema example has been made more concrete and is consistent with similar updates in api/openapi.cloud.yaml and the JavaScript client schemas, improving clarity for API consumers.

Please verify that in_arrears is a valid enum value for the paymentTerm field in the Price schema definition, and that the example structure satisfies all schema requirements.

api/openapi.cloud.yaml (1)

23922-23925: No issues found. The Price schema example is valid.

The example correctly represents a FlatPriceWithPaymentTerm object with all required fields (type, amount) and an optional paymentTerm value that matches the defined enum (in_advance, in_arrears). The structure complies with the schema definition.

api/client/python/openmeter/aio/__init__.py (1)

9-9: Verify the necessity of # type: ignore comment.

The # type: ignore comment suppresses type checking for the OpenMeterCloudClient import. Ensure this is intentional and not hiding a legitimate type checking issue such as a circular import or missing type stub.

Consider whether the type checking issue can be resolved properly rather than suppressed. If the suppression is necessary (e.g., due to generated code limitations), consider adding a more specific comment explaining why.

api/client/python/openmeter/_configuration.py (2)

23-33: Configuration initialization looks good.

The credential validation and configuration setup are properly implemented. The kwargs.setdefault call ensures the SDK moniker is set before policy configuration.


35-45: Policy configuration follows corehttp patterns.

The policy setup correctly builds default policies and creates a ServiceKeyCredentialPolicy with Bearer authentication when no custom authentication policy is provided.

api/client/python/openmeter/aio/_configuration.py (1)

23-45: Async configuration correctly mirrors sync implementation.

The async configuration properly uses AsyncRetryPolicy while maintaining identical structure to the sync version. This consistency is good for maintainability.

api/client/python/openmeter/models/__init__.py (1)

1-728: Public model API surface looks complete.

The module correctly exposes all model classes and enums, integrates patching hooks, and follows standard Python SDK patterns for public API management.

api/client/python/examples/async/ingest.py (1)

19-43: Async client usage and error handling look good.

The async context manager usage, Event construction, and error handling are properly implemented following async/await patterns.

api/client/python/openmeter/operations/__init__.py (1)

1-27: Operations module initialization is correct.

The module properly exposes all operation groups and integrates patching hooks following standard SDK patterns.

api/client/python/openmeter/_client.py (2)

42-68: Client initialization follows corehttp patterns.

The client properly initializes configuration, policies, and operation groups. The template endpoint "{endpoint}" at line 45 is correctly used with format_url in send_request.


70-94: send_request implementation is correct.

The method properly deep-copies the request, formats the URL with the endpoint parameter, and forwards to the pipeline client.

api/client/python/openmeter/aio/_client.py (2)

42-68: Async client initialization correctly mirrors sync implementation.

The async client properly uses AsyncPipelineClient and async operation groups while maintaining the same initialization pattern as the sync client.


70-106: Async send_request and lifecycle methods are properly implemented.

The async methods correctly use await, return appropriate types, and properly manage the async pipeline client lifecycle.

api/client/python/openmeter/models/_enums.py (1)

35-48: Enums and case-insensitive meta look good.

api/client/python/openmeter/_utils/serialization.py (1)

589-601: XML attribute meta key mismatch ("attr" vs "attribute").

This file checks xml_desc.get("attr"), while model_base uses "attribute". Inconsistent keys will break XML attribute handling. Align on "attribute". Based on learnings.

Apply:

-                        if xml_desc.get("attr", False):
+                        if xml_desc.get("attribute", False):
                             if xml_ns:
                                 ET.register_namespace(xml_prefix, xml_ns)
                                 xml_name = "{{{}}}{}".format(xml_ns, xml_name)
                             serialized.set(xml_name, new_attr)  # type: ignore
-    if xml_desc.get("attr", False):
+    if xml_desc.get("attribute", False):
         return data.get(xml_name)

Verify usage across repo:

Also applies to: 1299-1304

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (6)
api/client/python/examples/sync/query.py (4)

10-13: Validate token before creating credential.

If OPENMETER_TOKEN is not set, token will be None, and the client configuration will raise a ValueError. This issue was previously identified in past reviews.


24-25: Check if data is empty before accessing.

Accessing r.data[0] will raise an IndexError if the query returns no data. This issue was previously identified in past reviews.


33-34: Check if 'model' key exists before accessing.

Accessing row.group_by["model"] will raise a KeyError if the "model" key is not present in the dictionary. This issue was previously identified in past reviews.


37-41: Check if data is empty before accessing.

Accessing r.data[0] will raise an IndexError if the query returns no data for the filtered results. This issue was previously identified in past reviews.

api/client/python/examples/async/query.py (1)

10-13: Add token validation before creating ServiceKeyCredential.

The token can be None when OPENMETER_TOKEN is not set, which will cause a runtime error when passed to ServiceKeyCredential.

Apply this diff:

 ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud"
 token: Optional[str] = environ.get("OPENMETER_TOKEN")
 
+if not token:
+    raise ValueError("OPENMETER_TOKEN environment variable must be set")
+
 credential = ServiceKeyCredential(token)
api/client/python/examples/async/ingest.py (1)

12-15: Add token validation before creating ServiceKeyCredential.

The token can be None when OPENMETER_TOKEN is not set, which will cause a runtime error when passed to ServiceKeyCredential.

Apply this diff:

 ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud"
 token: Optional[str] = environ.get("OPENMETER_TOKEN")
 
+if not token:
+    raise ValueError("OPENMETER_TOKEN environment variable must be set")
+
 credential = ServiceKeyCredential(token)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7f045e and ee13189.

📒 Files selected for processing (17)
  • api/client/python/examples/async/ingest.py (1 hunks)
  • api/client/python/examples/async/query.py (1 hunks)
  • api/client/python/examples/sync/ingest.py (1 hunks)
  • api/client/python/examples/sync/query.py (1 hunks)
  • api/client/python/openmeter/__init__.py (1 hunks)
  • api/client/python/openmeter/_client.py (1 hunks)
  • api/client/python/openmeter/_configuration.py (1 hunks)
  • api/client/python/openmeter/_types.py (1 hunks)
  • api/client/python/openmeter/aio/__init__.py (1 hunks)
  • api/client/python/openmeter/aio/_client.py (1 hunks)
  • api/client/python/openmeter/aio/_configuration.py (1 hunks)
  • api/client/python/openmeter/aio/operations/__init__.py (1 hunks)
  • api/client/python/openmeter/operations/__init__.py (1 hunks)
  • api/spec/package.json (2 hunks)
  • api/spec/src/auth.tsp (1 hunks)
  • api/spec/tspconfig.client.yaml (1 hunks)
  • api/spec/tspconfig.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/spec/tspconfig.yaml
  • api/client/python/openmeter/_types.py
🧰 Additional context used
🧬 Code graph analysis (12)
api/client/python/openmeter/aio/operations/__init__.py (2)
api/client/python/openmeter/aio/operations/_operations.py (13)
  • AppOperations (197-219)
  • CustomerOperations (222-245)
  • ProductCatalogOperations (248-276)
  • EntitlementsOperations (279-305)
  • BillingOperations (308-337)
  • PortalOperations (340-358)
  • NotificationOperations (361-380)
  • InfoOperations (383-401)
  • EventsOperations (404-904)
  • EventsV2Operations (907-1030)
  • MetersOperations (1033-2248)
  • SubjectsOperations (2251-2634)
  • DebugOperations (2637-2731)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/examples/sync/query.py (3)
api/client/python/openmeter/_client.py (1)
  • OpenMeterClient (30-134)
api/client/python/openmeter/models/_models.py (2)
  • MeterQueryResult (9615-9671)
  • FilterString (6504-6600)
api/client/python/openmeter/operations/_operations.py (2)
  • query_json (5469-5622)
  • query_json (19843-19990)
api/client/python/examples/sync/ingest.py (4)
api/client/python/openmeter/_client.py (1)
  • OpenMeterClient (30-134)
api/client/python/openmeter/models/_models.py (1)
  • Event (6140-6219)
api/client/python/examples/async/ingest.py (1)
  • main (18-43)
api/client/python/openmeter/operations/_operations.py (4)
  • ingest_event (4433-4448)
  • ingest_event (4451-4464)
  • ingest_event (4467-4482)
  • ingest_event (4484-4557)
api/client/python/examples/async/ingest.py (4)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterClient (30-136)
api/client/python/openmeter/models/_models.py (1)
  • Event (6140-6219)
api/client/python/openmeter/aio/operations/_operations.py (4)
  • ingest_event (550-565)
  • ingest_event (568-583)
  • ingest_event (586-601)
  • ingest_event (603-674)
api/client/python/examples/sync/ingest.py (1)
  • main (22-43)
api/client/python/openmeter/operations/__init__.py (2)
api/client/python/openmeter/operations/_operations.py (13)
  • AppOperations (4080-4102)
  • CustomerOperations (4105-4128)
  • ProductCatalogOperations (4131-4159)
  • EntitlementsOperations (4162-4188)
  • BillingOperations (4191-4220)
  • PortalOperations (4223-4241)
  • NotificationOperations (4244-4263)
  • InfoOperations (4266-4284)
  • EventsOperations (4287-4789)
  • EventsV2Operations (4792-4915)
  • MetersOperations (4918-6133)
  • SubjectsOperations (6136-6519)
  • DebugOperations (6522-6616)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_client.py (4)
api/client/python/openmeter/_configuration.py (1)
  • OpenMeterClientConfiguration (11-43)
api/client/python/openmeter/aio/_configuration.py (1)
  • OpenMeterClientConfiguration (11-43)
api/client/python/openmeter/aio/operations/_operations.py (13)
  • AppOperations (197-219)
  • BillingOperations (308-337)
  • CustomerOperations (222-245)
  • DebugOperations (2637-2731)
  • EntitlementsOperations (279-305)
  • EventsOperations (404-904)
  • EventsV2Operations (907-1030)
  • InfoOperations (383-401)
  • MetersOperations (1033-2248)
  • NotificationOperations (361-380)
  • PortalOperations (340-358)
  • ProductCatalogOperations (248-276)
  • SubjectsOperations (2251-2634)
api/client/python/openmeter/_client.py (3)
  • OpenMeterClient (30-134)
  • send_request (100-124)
  • close (126-127)
api/client/python/openmeter/aio/_configuration.py (1)
api/client/python/openmeter/_configuration.py (2)
  • OpenMeterClientConfiguration (11-43)
  • _configure (33-43)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterClient (30-136)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/_client.py (5)
api/client/python/openmeter/_configuration.py (1)
  • OpenMeterClientConfiguration (11-43)
api/client/python/openmeter/aio/_configuration.py (1)
  • OpenMeterClientConfiguration (11-43)
api/client/python/openmeter/_utils/serialization.py (5)
  • Deserializer (1350-2024)
  • Serializer (472-1170)
  • _serialize (527-637)
  • _deserialize (1404-1475)
  • url (682-705)
api/client/python/openmeter/aio/operations/_operations.py (4)
  • AppOperations (197-219)
  • BillingOperations (308-337)
  • CustomerOperations (222-245)
  • SubjectsOperations (2251-2634)
api/client/python/openmeter/operations/_operations.py (4)
  • AppOperations (4080-4102)
  • BillingOperations (4191-4220)
  • CustomerOperations (4105-4128)
  • SubjectsOperations (6136-6519)
api/client/python/openmeter/__init__.py (7)
api/client/python/openmeter/_client.py (1)
  • OpenMeterClient (30-134)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterClient (30-136)
api/client/python/openmeter/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/aio/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/models/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/operations/_patch.py (1)
  • patch_sdk (12-18)
api/client/python/openmeter/_configuration.py (2)
api/client/python/openmeter/aio/_configuration.py (2)
  • OpenMeterClientConfiguration (11-43)
  • _configure (33-43)
api/client/python/openmeter/_utils/model_base.py (3)
  • setdefault (446-446)
  • setdefault (449-449)
  • setdefault (451-461)
api/client/python/examples/async/query.py (5)
api/client/python/openmeter/aio/_client.py (1)
  • OpenMeterClient (30-136)
api/client/python/openmeter/models/_models.py (1)
  • MeterQueryResult (9615-9671)
api/client/python/openmeter/aio/operations/_operations.py (18)
  • get (1152-1228)
  • get (2341-2417)
  • get (2836-2913)
  • get (3704-3778)
  • get (5384-5469)
  • get (5835-5912)
  • get (6575-6651)
  • get (7175-7257)
  • get (8035-8114)
  • get (8643-8725)
  • get (10313-10392)
  • get (10767-10844)
  • get (11420-11500)
  • get (13125-13211)
  • get (13728-13820)
  • get (16621-16697)
  • query_json (1584-1737)
  • query_json (15970-16117)
api/client/python/examples/async/ingest.py (1)
  • main (18-43)
api/client/python/examples/sync/query.py (1)
  • main (21-43)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Artifacts / Benthos Collector Container image
  • GitHub Check: Lint
  • GitHub Check: Test
  • GitHub Check: Code Generators
  • GitHub Check: Migration Checks
  • GitHub Check: Build
  • GitHub Check: Repository Scan
  • GitHub Check: Analyze (go)
🔇 Additional comments (21)
api/spec/src/auth.tsp (1)

3-7: LGTM!

The new TokenAuth model is correctly defined and extends BearerAuth with an appropriate friendly name. This aligns well with the token-based authentication pattern used throughout the Python client.

api/client/python/openmeter/operations/__init__.py (1)

1-43: LGTM!

The package initialization follows best practices for generated SDKs:

  • Proper use of TYPE_CHECKING for type hint imports
  • Clean separation of operation class exports
  • Correct patch integration with runtime initialization
api/spec/tspconfig.client.yaml (1)

1-20: LGTM!

The TypeSpec configuration correctly defines the Python HTTP client emitter with appropriate package metadata and output directories.

api/spec/package.json (2)

6-10: LGTM!

The script restructuring into separate compile:openapi and compile:client commands provides better granularity and aligns with the dual-output approach (OpenAPI spec + Python client).


22-30: LGTM!

The dependency updates are consistent, with all @typespec/* packages aligned at version 1.5.0 (except rest/versioning at 0.75.0), and the new @typespec/http-client-python@0.19.1 supports the Python client generation objective.

api/client/python/examples/sync/ingest.py (1)

22-43: LGTM!

The event creation and ingestion logic is correct, with proper CloudEvents structure and error handling for HttpResponseError.

api/client/python/openmeter/aio/__init__.py (1)

1-23: LGTM!

The async package initialization correctly implements the patch integration pattern with:

  • Proper TYPE_CHECKING usage for static analysis
  • Graceful fallback when patch imports fail
  • Runtime patch initialization via _patch_sdk()
api/client/python/openmeter/_configuration.py (2)

23-31: LGTM!

The configuration initialization correctly validates that the credential is not None and properly sets up the endpoint and SDK moniker.


33-43: LGTM!

The policy configuration correctly sets up the HTTP pipeline with appropriate defaults and automatically creates a ServiceKeyCredentialPolicy with Bearer authentication when a credential is provided.

api/client/python/openmeter/aio/_configuration.py (1)

11-43: LGTM!

The async configuration correctly uses AsyncRetryPolicy (line 38) instead of the sync RetryPolicy, and properly validates the credential parameter. The policy stack setup aligns with the sync version while accommodating async-specific needs.

api/client/python/examples/async/query.py (1)

16-42: LGTM!

The async query examples correctly demonstrate the OpenMeterClient API usage with proper error handling and context management.

api/client/python/examples/async/ingest.py (1)

18-43: LGTM!

The Event construction and ingest flow correctly demonstrates the new API with proper async context management and error handling.

api/client/python/openmeter/aio/operations/__init__.py (1)

1-43: LGTM!

The operations module properly exports all operation classes and integrates the patching mechanism. The TYPE_CHECKING guard and runtime patch invocation follow standard Python SDK patterns.

api/client/python/openmeter/__init__.py (2)

14-19: Consider moving patch_sdk import inside the try block.

The from ._patch import patch_sdk as _patch_sdk import on line 19 is outside the try/except block, which could raise an ImportError if _patch.py has issues, while the earlier imports are protected. This inconsistency could lead to unexpected failures.

Apply this diff to ensure consistent error handling:

 try:
     from ._patch import __all__ as _patch_all
     from ._patch import *
+    from ._patch import patch_sdk as _patch_sdk
 except ImportError:
     _patch_all = []
-from ._patch import patch_sdk as _patch_sdk

21-26: LGTM!

The package exports and patch invocation are correctly structured. The __all__ list is properly extended with patched exports.

api/client/python/openmeter/_client.py (3)

30-98: LGTM!

The synchronous OpenMeterClient is well-structured with:

  • Comprehensive operation group initialization
  • Proper credential and configuration handling via OpenMeterClientConfiguration
  • Consistent serializer/deserializer sharing across operations
  • Client-side validation disabled as intended (line 85)

100-124: LGTM!

The send_request method correctly handles custom requests by formatting the endpoint URL and delegating to the pipeline client. The documentation example is helpful.


126-134: LGTM!

The lifecycle methods properly support context manager usage and delegate to the underlying pipeline client. The use of Self for __enter__ return type is correct.

api/client/python/openmeter/aio/_client.py (3)

30-98: LGTM!

The async OpenMeterClient mirrors the sync version correctly with:

  • AsyncPipelineClient for async operations (line 81)
  • Proper credential handling via OpenMeterClientConfiguration
  • All operation groups initialized with shared serializers
  • Client-side validation disabled (line 85)

100-126: LGTM!

The async send_request method correctly:

  • Returns Awaitable[AsyncHttpResponse] (line 102)
  • Formats the endpoint URL consistently with the sync version
  • Updates documentation example to show await usage (line 108)

128-136: LGTM!

The async lifecycle methods properly implement the async context manager protocol with all operations correctly awaiting the underlying pipeline client methods.

@tothandras tothandras force-pushed the feat/python-sdk-generator branch 2 times, most recently from d637bc9 to 6cda475 Compare October 28, 2025 14:33
@tothandras tothandras force-pushed the feat/python-sdk-generator branch from 6cda475 to f630c5b Compare October 28, 2025 14:37
hekike
hekike previously approved these changes Oct 28, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
api/client/python/openmeter/_types.py (1)

28-28: Remove duplicate str in Union.

This issue was already raised in a previous review.

🧹 Nitpick comments (8)
api/client/python/examples/async/query.py (1)

9-17: Consider documenting or validating the token requirement.

The token can be None if OPENMETER_TOKEN is not set. While the Client handles this gracefully during initialization (by not setting an authentication policy), API calls will fail with authentication errors. For better user experience in examples, consider either:

  • Adding a comment documenting that OPENMETER_TOKEN must be set
  • Adding validation that prints a helpful message before attempting API calls

Example validation approach:

 ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud"
 token: Optional[str] = environ.get("OPENMETER_TOKEN")
+if not token:
+    print("Error: OPENMETER_TOKEN environment variable must be set")
+    exit(1)

Note: This pattern appears in multiple example files (async/query.py, async/entitlement.py, async/subscription.py, sync/ingest.py, sync/customer.py). Consider applying consistently across all examples.

api/client/python/examples/sync/ingest.py (1)

14-17: Consider using context manager for resource cleanup.

While the module-level Client works for this simple example, using a context manager would ensure proper connection cleanup and maintain consistency with the async examples. This is optional but recommended.

Example:

-client = Client(
-    endpoint=ENDPOINT,
-    token=token,
-)
-
-
 def main() -> None:
+    with Client(endpoint=ENDPOINT, token=token) as client:
-    try:
-        # Create a CloudEvents event
-        event = Event(
+        try:
+            # Create a CloudEvents event
+            event = Event(
api/client/python/examples/sync/customer.py (1)

17-20: Consider using context manager for consistency.

Similar to sync/ingest.py, using a context manager would ensure proper resource cleanup and maintain consistency with async examples.

Example:

-client = Client(
-    endpoint=ENDPOINT,
-    token=token,
-)
-
-
 def main() -> None:
+    with Client(endpoint=ENDPOINT, token=token) as client:
-    try:
-        # Create a customer
+        try:
+            # Create a customer
api/client/python/examples/sync/entitlement.py (1)

1-15: Add if __name__ == '__main__' guard for script execution.

While not critical for an example script, it's a Python best practice to guard direct execution:

-main()
+if __name__ == '__main__':
+    main()

This pattern prevents unintended execution when the module is imported and is consistent with Python conventions.

Note: The token parameter is Optional and the Client handles None gracefully by not setting up authentication. If authentication is required for this example to work, consider adding explicit validation with a helpful error message.

api/client/python/examples/sync/subscription.py (1)

1-20: Add if __name__ == '__main__' guard for script execution.

Apply the same best practice as suggested for other example scripts:

-main()
+if __name__ == '__main__':
+    main()
api/client/python/examples/async/customer.py (1)

1-16: Add if __name__ == '__main__' guard.

For consistency with Python best practices:

-asyncio.run(main())
+if __name__ == '__main__':
+    asyncio.run(main())
api/client/python/examples/async/ingest.py (1)

11-19: Add if __name__ == '__main__' guard.

For consistency with Python conventions:

-asyncio.run(main())
+if __name__ == '__main__':
+    asyncio.run(main())

Note: The past review comment about token validation flagged a concern that ServiceKeyCredential might fail with None. However, the Client implementation (in _client.py lines 11-30) handles None tokens gracefully by only creating the credential when token is truthy. While technically safe, explicit validation with a helpful error message would improve the example's user experience if authentication is required for the API to work.

api/spec/patches/@typespec__openapi.patch (1)

1-44: Document the rationale and maintenance plan for this dependency patch.

Applying patches to third-party packages like @typespec/openapi introduces maintenance overhead. When the package is updated, this patch will need to be reviewed and potentially reapplied.

Please consider:

  1. Adding a README or comment in the patches directory explaining why this patch is needed
  2. Documenting any plans to contribute this upstream
  3. Adding validation tests to detect if future package updates break this patch
  4. Evaluating if there are alternative approaches (e.g., configuration options, custom plugins) that avoid patching

Would you like me to help create documentation template or open an issue to track upstreaming this change?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee13189 and f630c5b.

⛔ Files ignored due to path filters (26)
  • api/client/python/examples/async/poetry.lock is excluded by !**/*.lock
  • api/client/python/examples/poetry.lock is excluded by !**/*.lock
  • api/client/python/openmeter/_generated/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_client.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_configuration.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_utils/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_utils/model_base.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_utils/serialization.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/_version.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_client.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_configuration.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_enums.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_models.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_patch.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/_patch.py is excluded by !**/_generated/**
  • api/client/python/poetry.lock is excluded by !**/*.lock
  • api/spec/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (49)
  • api/client/go/client.gen.go (1 hunks)
  • api/client/javascript/src/client/schemas.ts (1 hunks)
  • api/client/python/.gitattributes (1 hunks)
  • api/client/python/.gitignore (1 hunks)
  • api/client/python/MANIFEST.in (1 hunks)
  • api/client/python/README.md (2 hunks)
  • api/client/python/config.yaml (0 hunks)
  • api/client/python/examples/README.md (1 hunks)
  • api/client/python/examples/async/customer.py (1 hunks)
  • api/client/python/examples/async/entitlement.py (1 hunks)
  • api/client/python/examples/async/ingest.py (1 hunks)
  • api/client/python/examples/async/pyproject.toml (0 hunks)
  • api/client/python/examples/async/query.py (1 hunks)
  • api/client/python/examples/async/subscription.py (1 hunks)
  • api/client/python/examples/pyproject.toml (1 hunks)
  • api/client/python/examples/sync/customer.py (1 hunks)
  • api/client/python/examples/sync/entitlement.py (1 hunks)
  • api/client/python/examples/sync/ingest.py (1 hunks)
  • api/client/python/examples/sync/query.py (1 hunks)
  • api/client/python/examples/sync/subscription.py (1 hunks)
  • api/client/python/openmeter/__init__.py (1 hunks)
  • api/client/python/openmeter/_client.py (1 hunks)
  • api/client/python/openmeter/_types.py (1 hunks)
  • api/client/python/openmeter/_version.py (1 hunks)
  • api/client/python/openmeter/aio/__init__.py (1 hunks)
  • api/client/python/openmeter/aio/_client.py (1 hunks)
  • api/client/python/openmeter/aio/operations/__init__.py (1 hunks)
  • api/client/python/openmeter/models/__init__.py (1 hunks)
  • api/client/python/openmeter/operations/__init__.py (1 hunks)
  • api/client/python/pyproject.toml (1 hunks)
  • api/client/python/src/openmeter/__init__.py (0 hunks)
  • api/client/python/src/openmeter/_client.py (0 hunks)
  • api/client/python/src/openmeter/_configuration.py (0 hunks)
  • api/client/python/src/openmeter/_operations/__init__.py (0 hunks)
  • api/client/python/src/openmeter/_operations/_patch.py (0 hunks)
  • api/client/python/src/openmeter/_vendor.py (0 hunks)
  • api/client/python/src/openmeter/aio/__init__.py (0 hunks)
  • api/client/python/src/openmeter/aio/_client.py (0 hunks)
  • api/client/python/src/openmeter/aio/_configuration.py (0 hunks)
  • api/client/python/src/openmeter/aio/_operations/__init__.py (0 hunks)
  • api/client/python/src/openmeter/aio/_vendor.py (0 hunks)
  • api/openapi.cloud.yaml (1 hunks)
  • api/openapi.yaml (1 hunks)
  • api/spec/package.json (2 hunks)
  • api/spec/patches/@typespec__openapi.patch (1 hunks)
  • api/spec/src/query.tsp (0 hunks)
  • api/spec/tspconfig.client.yaml (1 hunks)
  • api/spec/tspconfig.yaml (1 hunks)
  • flake.nix (1 hunks)
💤 Files with no reviewable changes (14)
  • api/spec/src/query.tsp
  • api/client/python/config.yaml
  • api/client/python/src/openmeter/aio/_vendor.py
  • api/client/python/src/openmeter/aio/_operations/init.py
  • api/client/python/examples/async/pyproject.toml
  • api/client/python/src/openmeter/aio/_configuration.py
  • api/client/python/src/openmeter/aio/init.py
  • api/client/python/src/openmeter/_configuration.py
  • api/client/python/src/openmeter/aio/_client.py
  • api/client/python/src/openmeter/_operations/_patch.py
  • api/client/python/src/openmeter/init.py
  • api/client/python/src/openmeter/_vendor.py
  • api/client/python/src/openmeter/_operations/init.py
  • api/client/python/src/openmeter/_client.py
✅ Files skipped from review due to trivial changes (1)
  • api/client/python/examples/README.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • api/client/javascript/src/client/schemas.ts
  • api/spec/tspconfig.client.yaml
  • api/spec/tspconfig.yaml
  • api/client/python/examples/sync/query.py
  • api/spec/package.json
  • api/client/python/openmeter/_version.py
  • flake.nix
  • api/client/python/examples/pyproject.toml
  • api/client/python/openmeter/_client.py
  • api/client/python/openmeter/aio/_client.py
  • api/client/python/MANIFEST.in
🧰 Additional context used
🧬 Code graph analysis (15)
api/client/python/examples/async/ingest.py (3)
api/client/python/openmeter/aio/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Event (6197-6276)
api/client/python/openmeter/_generated/aio/operations/_operations.py (19)
  • get (1164-1261)
  • get (2594-2691)
  • get (3208-3306)
  • get (4282-4376)
  • get (6273-6375)
  • get (6802-6900)
  • get (7658-7755)
  • get (8386-8491)
  • get (9443-9544)
  • get (10176-10281)
  • get (12238-12339)
  • get (12758-12857)
  • get (13524-13627)
  • get (15503-15610)
  • get (16209-16318)
  • ingest_event (442-461)
  • ingest_event (464-483)
  • ingest_event (486-505)
  • ingest_event (508-595)
api/client/python/openmeter/__init__.py (2)
api/client/python/openmeter/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/examples/sync/customer.py (3)
api/client/python/openmeter/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (3)
  • CustomerCreate (3004-3068)
  • CustomerReplaceUpdate (3182-3246)
  • CustomerUsageAttribution (3249-3280)
api/client/python/examples/async/customer.py (1)
  • main (19-71)
api/client/python/examples/async/entitlement.py (2)
api/client/python/openmeter/aio/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/aio/operations/_operations.py (18)
  • get (1164-1261)
  • get (2594-2691)
  • get (3208-3306)
  • get (4282-4376)
  • get (6273-6375)
  • get (6802-6900)
  • get (7658-7755)
  • get (8386-8491)
  • get (9443-9544)
  • get (10176-10281)
  • get (12238-12339)
  • get (12758-12857)
  • get (13524-13627)
  • get (15503-15610)
  • get (16209-16318)
  • get_customer_entitlement_value (14965-15073)
  • get_customer_entitlement_value (22871-22979)
  • get_customer_access (14845-14942)
api/client/python/examples/async/customer.py (3)
api/client/python/openmeter/aio/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (3)
  • CustomerCreate (3004-3068)
  • CustomerReplaceUpdate (3182-3246)
  • CustomerUsageAttribution (3249-3280)
api/client/python/openmeter/_generated/aio/operations/_operations.py (46)
  • get (1164-1261)
  • get (2594-2691)
  • get (3208-3306)
  • get (4282-4376)
  • get (6273-6375)
  • get (6802-6900)
  • get (7658-7755)
  • get (8386-8491)
  • get (9443-9544)
  • get (10176-10281)
  • get (12238-12339)
  • get (12758-12857)
  • get (13524-13627)
  • get (15503-15610)
  • get (16209-16318)
  • create (1266-1285)
  • create (1288-1307)
  • create (1310-1329)
  • create (1332-1434)
  • create (5951-5970)
  • create (5973-5992)
  • create (5995-6014)
  • create (6017-6119)
  • create (7466-7490)
  • create (7493-7517)
  • create (7520-7544)
  • create (7547-7654)
  • create (8025-8044)
  • create (8047-8066)
  • create (8069-8088)
  • create (8091-8193)
  • update (1439-1461)
  • update (1464-1486)
  • update (1489-1511)
  • update (1514-1620)
  • update (3311-3334)
  • update (3337-3360)
  • update (3363-3386)
  • update (3389-3504)
  • update (6380-6402)
  • update (6405-6427)
  • update (6430-6452)
  • update (6455-6565)
  • update (8198-8220)
  • update (8223-8245)
  • update (8248-8270)
api/client/python/examples/sync/ingest.py (3)
api/client/python/openmeter/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Event (6197-6276)
api/client/python/openmeter/_generated/operations/_operations.py (4)
  • ingest_event (5448-5467)
  • ingest_event (5470-5489)
  • ingest_event (5492-5511)
  • ingest_event (5514-5601)
api/client/python/openmeter/models/__init__.py (1)
api/client/python/openmeter/_generated/models/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/openmeter/operations/__init__.py (5)
api/client/python/openmeter/_generated/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/openmeter/_generated/aio/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/openmeter/_generated/aio/operations/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/openmeter/_generated/models/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/openmeter/_generated/operations/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/examples/sync/subscription.py (4)
api/client/python/openmeter/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (2)
  • PlanSubscriptionCreate (12097-12182)
  • PlanReferenceInput (11919-11953)
api/client/python/openmeter/_generated/models/_enums.py (1)
  • SubscriptionStatus (640-647)
api/client/python/openmeter/_generated/operations/_operations.py (2)
  • get_expanded (15600-15703)
  • list_customer_subscriptions (11661-11785)
api/client/python/examples/async/query.py (3)
api/client/python/openmeter/aio/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (1)
  • MeterQueryResult (9803-9857)
api/client/python/openmeter/_generated/aio/operations/_operations.py (17)
  • get (1164-1261)
  • get (2594-2691)
  • get (3208-3306)
  • get (4282-4376)
  • get (6273-6375)
  • get (6802-6900)
  • get (7658-7755)
  • get (8386-8491)
  • get (9443-9544)
  • get (10176-10281)
  • get (12238-12339)
  • get (12758-12857)
  • get (13524-13627)
  • get (15503-15610)
  • get (16209-16318)
  • query_json (1707-1878)
  • query_json (18912-19077)
api/client/python/examples/async/subscription.py (4)
api/client/python/openmeter/aio/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/models/_models.py (2)
  • PlanSubscriptionCreate (12097-12182)
  • PlanReferenceInput (11919-11953)
api/client/python/openmeter/_generated/models/_enums.py (1)
  • SubscriptionStatus (640-647)
api/client/python/openmeter/_generated/aio/operations/_operations.py (33)
  • get (1164-1261)
  • get (2594-2691)
  • get (3208-3306)
  • get (4282-4376)
  • get (6273-6375)
  • get (6802-6900)
  • get (7658-7755)
  • get (8386-8491)
  • get (9443-9544)
  • get (10176-10281)
  • get (12238-12339)
  • get (12758-12857)
  • get (13524-13627)
  • get (15503-15610)
  • get (16209-16318)
  • create (1266-1285)
  • create (1288-1307)
  • create (1310-1329)
  • create (1332-1434)
  • create (5951-5970)
  • create (5973-5992)
  • create (5995-6014)
  • create (6017-6119)
  • create (7466-7490)
  • create (7493-7517)
  • create (7520-7544)
  • create (7547-7654)
  • create (8025-8044)
  • create (8047-8066)
  • create (8069-8088)
  • create (8091-8193)
  • get_expanded (10594-10697)
  • list_customer_subscriptions (6655-6779)
api/client/python/openmeter/aio/operations/__init__.py (1)
api/client/python/openmeter/_generated/aio/operations/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/examples/sync/entitlement.py (2)
api/client/python/openmeter/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/operations/_operations.py (3)
  • get_customer_entitlement_value (19971-20079)
  • get_customer_entitlement_value (27877-27985)
  • get_customer_access (19851-19948)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
  • Client (12-31)
api/client/python/openmeter/_generated/aio/_patch.py (1)
  • patch_sdk (14-20)
api/client/python/openmeter/_types.py (3)
api/client/go/client.gen.go (30)
  • App (961-963)
  • CustomerAppData (2091-2093)
  • RateCardEntitlement (5784-5786)
  • RateCardUsageBasedPrice (5919-5921)
  • RateCard (5768-5770)
  • RecurringPeriodInterval (5945-5947)
  • Entitlement (2386-2388)
  • SubscriptionTiming (6779-6781)
  • SubscriptionEditOperation (6554-6556)
  • EntitlementV2 (3093-3095)
  • MeasureUsageFrom (4487-4489)
  • BillingWorkflowCollectionAlignment (1399-1399)
  • BillingProfileAppsOrReference (1240-1242)
  • InvoiceDocumentRef (3752-3752)
  • BillingDiscountReason (1069-1071)
  • PaymentTerms (5347-5349)
  • NotificationChannel (4707-4707)
  • NotificationRule (4978-4980)
  • NotificationEventPayload (4952-4954)
  • AppReplaceUpdate (1009-1011)
  • ULIDOrExternalKey (6843-6843)
  • ListFeaturesResult (4419-4421)
  • SubscriptionCreate (6539-6541)
  • SubscriptionChange (6525-6527)
  • EntitlementV2CreateInputs (3098-3100)
  • ListEntitlementsResult (4411-4413)
  • EntitlementCreateInputs (2508-2510)
  • NotificationChannelCreateRequest (4710-4710)
  • NotificationRuleCreateRequest (5069-5071)
  • IngestEventsBody (3425-3427)
api/client/javascript/src/client/schemas.ts (29)
  • App (11526-11526)
  • CustomerAppData (11673-11673)
  • RateCardEntitlement (11992-11992)
  • RateCardUsageBasedPrice (11999-12000)
  • RateCard (11989-11989)
  • RecurringPeriodInterval (12004-12005)
  • Entitlement (11714-11714)
  • SubscriptionTiming (12069-12069)
  • SubscriptionEditOperation (12056-12057)
  • EntitlementV2 (11741-11741)
  • MeasureUsageFrom (11865-11865)
  • BillingWorkflowCollectionAlignment (11583-11584)
  • BillingProfileAppsOrReference (11558-11559)
  • InvoiceDocumentRef (11796-11796)
  • BillingDiscountReason (11541-11542)
  • PaymentTerms (11957-11957)
  • NotificationChannel (11882-11882)
  • NotificationRule (11922-11922)
  • NotificationEventPayload (11916-11917)
  • AppReplaceUpdate (11531-11531)
  • ListFeaturesResult (11857-11857)
  • SubscriptionCreate (12054-12054)
  • SubscriptionChange (12051-12051)
  • EntitlementV2CreateInputs (11742-11743)
  • ListEntitlementsResult (11855-11856)
  • EntitlementCreateInputs (11719-11720)
  • NotificationChannelCreateRequest (11883-11884)
  • NotificationRuleCreateRequest (11931-11932)
  • IngestEventsBody (11775-11775)
api/api.gen.go (30)
  • App (1046-1048)
  • CustomerAppData (2261-2263)
  • RateCardEntitlement (6303-6305)
  • RateCardUsageBasedPrice (6441-6443)
  • RateCard (6287-6289)
  • RecurringPeriodInterval (6467-6469)
  • Entitlement (2585-2587)
  • SubscriptionTiming (7396-7398)
  • SubscriptionEditOperation (7135-7137)
  • EntitlementV2 (3379-3381)
  • MeasureUsageFrom (4954-4956)
  • BillingWorkflowCollectionAlignment (1566-1566)
  • BillingProfileAppsOrReference (1380-1382)
  • InvoiceDocumentRef (4126-4126)
  • BillingDiscountReason (1209-1211)
  • PaymentTerms (5857-5859)
  • NotificationChannel (5178-5178)
  • NotificationRule (5449-5451)
  • NotificationEventPayload (5423-5425)
  • AppReplaceUpdate (1133-1135)
  • ULIDOrExternalKey (7482-7482)
  • ListFeaturesResult (4886-4888)
  • SubscriptionCreate (7120-7122)
  • SubscriptionChange (7106-7108)
  • EntitlementV2CreateInputs (3384-3386)
  • ListEntitlementsResult (4878-4880)
  • EntitlementCreateInputs (2753-2755)
  • NotificationChannelCreateRequest (5181-5181)
  • NotificationRuleCreateRequest (5540-5542)
  • IngestEventsBody (3775-3777)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: E2E
  • GitHub Check: Code Generators
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
🔇 Additional comments (24)
api/client/python/.gitignore (1)

1-19: Good organization and coverage for a Python project.

The .gitignore file is well-structured with clear section headers and standard patterns for Python packaging, caches, and virtual environments. The inclusion of .pyo and .pyd alongside *.pyc and the dual coverage of both .venv/ and venv/ demonstrates thoughtful handling of common Python development tools.

api/client/python/.gitattributes (1)

1-1: Pattern correctly updated to reflect Python client restructuring—no remaining generated files under old location.

Verification confirms:

  • Old src/openmeter/ directory completely removed
  • New openmeter/ directory with generated code in place (_generated/ subdirectory)
  • .gitattributes pattern openmeter/**/* appropriately marks all generated files in the new location
api/openapi.cloud.yaml (1)

23922-23925: Example is correct as-is.

The amount: '100' representation is correct. The Numeric schema explicitly defines amount as type: string with pattern ^\-?[0-9]+(\.[0-9]+)?$ for arbitrary precision handling. The example properly conforms to the schema definition.

api/openapi.yaml (1)

24862-24865: Good documentation improvement.

Replacing the placeholder example with a concrete, realistic Price object improves API documentation clarity for consumers. The example is well-formed and aligns with the same enhancement across sibling OpenAPI and client schema files.

api/client/python/openmeter/aio/__init__.py (1)

1-23: LGTM! Standard SDK patching pattern.

The dynamic import and patch initialization pattern is appropriate for a generated SDK with custom extensions. The TYPE_CHECKING guard ensures static type checkers can resolve symbols while maintaining runtime flexibility.

api/client/python/pyproject.toml (3)

3-3: Correct version format.

The version string now follows semantic versioning standards by removing the 'v' prefix.


24-30: Dependencies updated for corehttp migration.

The switch from Azure SDK dependencies to corehttp aligns with the broader Python client restructuring in this PR.


35-52: Appropriate Black configuration.

The configuration properly excludes the generated code directory from formatting while maintaining standard settings for the rest of the codebase.

api/client/python/examples/async/query.py (1)

19-46: Well-structured query examples.

The code demonstrates three different query patterns with appropriate data validation (checking r.data before access) and clear output messages. Error handling via HttpResponseError is also properly implemented.

api/client/python/examples/async/entitlement.py (1)

14-62: Clear entitlement workflow demonstration.

The example effectively demonstrates both specific feature entitlement checks and overall customer access retrieval. The conditional printing of optional fields (balance, usage, overage, config) properly handles the varying entitlement types. Error handling is appropriate.

api/client/python/examples/sync/ingest.py (1)

20-41: Correct Event structure and ingestion.

The CloudEvents-compliant Event creation includes all required fields with appropriate types. The ingestion call and error handling are straightforward and appropriate for an example.

api/client/python/examples/async/subscription.py (1)

19-77: Comprehensive subscription lifecycle example.

The example demonstrates creating, retrieving, and listing subscriptions with proper use of typed models. The conditional access to optional fields (like retrieved_subscription.plan) and the use of status filtering show good practices. Error handling is appropriate.

api/client/python/examples/sync/customer.py (1)

23-71: Clear customer management workflow.

The example effectively demonstrates creating, retrieving, and updating a customer with proper use of typed models (CustomerCreate, CustomerReplaceUpdate, CustomerUsageAttribution). The progression from EUR to USD currency and email updates clearly shows the update functionality.

api/client/python/README.md (1)

1-142: Well-structured documentation.

The README provides clear examples for both synchronous and asynchronous usage patterns. The progression from setup to basic operations (ingestion) to more complex queries is logical. All necessary imports are included, and the CloudEvents-compliant Event structure is properly documented.

api/client/python/examples/sync/entitlement.py (1)

18-61: LGTM! Clear example demonstrating entitlement checking.

The main function provides a well-structured demonstration of checking customer entitlements with proper error handling and informative output.

api/client/python/examples/sync/subscription.py (1)

23-73: LGTM! Comprehensive subscription example.

The example clearly demonstrates creating, retrieving, and listing subscriptions with proper error handling and informative output.

api/client/python/examples/async/customer.py (1)

19-71: LGTM! Well-structured async example with proper resource management.

The example correctly uses an async context manager for the client, ensuring proper resource cleanup. The customer CRUD flow is clear and properly demonstrates the async API.

api/client/python/openmeter/models/__init__.py (1)

1-30: LGTM! Standard SDK re-export pattern correctly implemented.

The module follows the Azure SDK pattern for re-exporting generated code with patch integration. The defensive import handling and all extension logic are correct.

api/client/python/openmeter/operations/__init__.py (1)

1-29: LGTM! Consistent with the SDK re-export pattern.

The operations re-export module follows the same pattern as the models module, maintaining consistency across the SDK structure.

api/client/python/examples/async/ingest.py (1)

15-40: LGTM! Clean async ingest example with proper resource management.

The example correctly constructs a CloudEvents-compliant Event and demonstrates the ingest API with proper async context management and error handling.

api/client/python/openmeter/aio/operations/__init__.py (1)

1-29: LGTM! Async operations re-export follows consistent pattern.

The async operations module correctly mirrors the sync operations structure with appropriate path adjustments for the aio package.

api/client/python/openmeter/__init__.py (1)

1-26: LGTM! Package initialization correctly exposes public API.

The main package init file properly exposes the Client class, wires versioning, and integrates the patch mechanism consistently with the rest of the SDK structure.

api/spec/patches/@typespec__openapi.patch (2)

40-42: LGTM: Dependency reordering for consistency.

The reordering of @typespec/library-linter to maintain alphabetical sorting improves maintainability.


18-21: Verify the behavioral impact and rationale for commenting out Scalar case handling in shouldInline.

The patch comments out the Scalar case in the shouldInline switch, changing inlining behavior: Scalars previously had conditional inlining (program.checker.isStdType(type) || isTemplateInstance(type)), but now always inline by falling through to the default case.

Based on TypeSpec's architecture, shouldInline determines whether to emit schema inline or as a component reference. Scalars require special handling because they carry format and constraint metadata; always inlining them (vs. conditional inlining) will produce different OpenAPI schemas.

However, the patch provides no context for why this change is necessary. The codebase contains no documentation, TODOs, or comments explaining the rationale.

Please provide or verify:

  • The reason this patch was needed (e.g., issue link, commit message, or PR description)
  • Whether this is a temporary workaround or permanent fix
  • Impact on the generated Python SDK schemas
  • Whether this should be contributed upstream

Comment on lines 49 to 51
BillingWorkflowCollectionAlignment = (
"_models.BillingWorkflowCollectionAlignmentSubscription"
)
Copy link
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 type alias assignments that currently assign string literals.

Lines 49-51, 55, 65, and 104 assign string literals instead of creating proper type aliases. With the TYPE_CHECKING guard preventing runtime import of _models, these assignments should be moved inside the TYPE_CHECKING block to function as type aliases.

Apply this diff:

 if TYPE_CHECKING:
     from . import models as _models
+    BillingWorkflowCollectionAlignment = _models.BillingWorkflowCollectionAlignmentSubscription
+    InvoiceDocumentRef = _models.CreditNoteOriginalInvoiceRef
+    NotificationChannel = _models.NotificationChannelWebhook
+    NotificationChannelCreateRequest = _models.NotificationChannelWebhookCreateRequest
+
 App = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"]
 ...
-BillingWorkflowCollectionAlignment = (
-    "_models.BillingWorkflowCollectionAlignmentSubscription"
-)
 BillingProfileAppsOrReference = Union[
     "_models.BillingProfileApps", "_models.BillingProfileAppReferences"
 ]
-InvoiceDocumentRef = "_models.CreditNoteOriginalInvoiceRef"
 BillingDiscountReason = Union[
 ...
-NotificationChannel = "_models.NotificationChannelWebhook"
 NotificationRule = Union[
 ...
-NotificationChannelCreateRequest = "_models.NotificationChannelWebhookCreateRequest"
 NotificationRuleCreateRequest = Union[

Also applies to: 55-55, 65-65, 104-104

🤖 Prompt for AI Agents
In api/client/python/openmeter/_types.py around lines 49-51, 55, 65, and 104,
several type alias variables are currently set to string literals (e.g.,
BillingWorkflowCollectionAlignment =
"_models.BillingWorkflowCollectionAlignmentSubscription") which makes them
runtime strings because imports of _models are guarded by TYPE_CHECKING; move
these assignments inside the existing TYPE_CHECKING block and replace the string
values with actual type aliases referencing the _models symbols (e.g.,
BillingWorkflowCollectionAlignment =
_models.BillingWorkflowCollectionAlignmentSubscription), removing the
quote-wrapped literals so they only exist for type checking.

Comment on lines 96 to 98
ListEntitlementsResult = Union[
list["_types.Entitlement"], "_models.EntitlementPaginatedResponse"
]
Copy link
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 incorrect module reference in ListEntitlementsResult.

Line 97 references "_types.Entitlement", which incorrectly refers to this module itself. Since Entitlement is a type alias defined in this module (line 29-33), reference it without a module prefix.

Apply this diff:

 ListEntitlementsResult = Union[
-    list["_types.Entitlement"], "_models.EntitlementPaginatedResponse"
+    list["Entitlement"], "_models.EntitlementPaginatedResponse"
 ]
📝 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
ListEntitlementsResult = Union[
list["_types.Entitlement"], "_models.EntitlementPaginatedResponse"
]
ListEntitlementsResult = Union[
list["Entitlement"], "_models.EntitlementPaginatedResponse"
]
🤖 Prompt for AI Agents
In api/client/python/openmeter/_types.py around lines 96 to 98, the Union
currently references the local Entitlement type as "_types.Entitlement" which is
incorrect; update the annotation to reference the local alias directly by
replacing "_types.Entitlement" with "Entitlement" (keep the forward-ref quotes
if needed), so the union becomes Union[list["Entitlement"],
"_models.EntitlementPaginatedResponse"].

@tothandras tothandras merged commit 0cf0897 into main Oct 28, 2025
26 checks passed
@tothandras tothandras deleted the feat/python-sdk-generator branch October 28, 2025 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants