Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
- `.nvmrc` is the GitHub Actions source of truth for Node. Keep it aligned with
`node -v` in the Nix `.#ci` shell.
- For slow or noisy commands, save complete output to a temporary log outside the repository, preserve and report the exit status and log path, inspect bounded slices, and never rerun solely because displayed output was truncated.
- `golangci-lint fmt` does not apply `wsl_v5` spacing rules. Use
`golangci-lint run --enable-only=wsl_v5 --fix <packages>` for those fixes, then
run normal lint for each affected module.

## Repository invariants

Expand Down Expand Up @@ -72,6 +75,9 @@

## Testing conventions

- Meter and feature-cost queries grouped by `customer_id` require a customer
filter to resolve usage attribution. Include the filter in e2e fixtures;
creating a customer with subject keys alone does not satisfy this requirement.
- PostgreSQL-backed tests require PostgreSQL and
`POSTGRES_HOST=127.0.0.1`; otherwise many suites silently skip.
- Root-package tests using `confluent-kafka-go` require `-tags=dynamic`. The
Expand Down
6 changes: 6 additions & 0 deletions api/spec/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ and the `options:` keys). The internal lib names in each `src/lib.ts` and their
`…:` state keys are separate identities used for diagnostics/state and have no
cross-package references.

Full `make update-openapi` also runs the legacy Python emitter, which requires its
npm-managed virtualenv. If that environment is unavailable during AIP-only work,
regenerate AIP, run the workspace formatter and the existing Makefile's OpenAPI
postprocessing/bundling, then run `go generate ./api/v3/...`. Report the full-target
limitation; do not hand-edit generated output or change the legacy SDK to bypass it.

## The emitted SDK: conventions the generator must reproduce

The hand-written runtime files and conformance tests under
Expand Down
17 changes: 9 additions & 8 deletions api/spec/packages/aip-client-javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,14 +414,15 @@ The full call path, HTTP route, and a short description are listed below.

### Features

| Method | HTTP | Description |
| --------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------- |
| `client.features.list` | `GET /openmeter/features` | List all features. |
| `client.features.create` | `POST /openmeter/features` | Create a feature. |
| `client.features.get` | `GET /openmeter/features/{featureId}` | Get a feature by id. |
| `client.features.update` | `PATCH /openmeter/features/{featureId}` | Update a feature by id. Currently only the unit_cost field can be updated. |
| `client.features.delete` | `DELETE /openmeter/features/{featureId}` | Delete a feature by id. |
| `client.features.queryCost` | `POST /openmeter/features/{featureId}/cost/query` | Query the cost of a feature. |
| Method | HTTP | Description |
| ------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.features.list` | `GET /openmeter/features` | List all features. |
| `client.features.create` | `POST /openmeter/features` | Create a feature. |
| `client.features.get` | `GET /openmeter/features/{featureId}` | Get a feature by id. |
| `client.features.update` | `PATCH /openmeter/features/{featureId}` | Update a feature by id. Currently only the unit_cost field can be updated. |
| `client.features.delete` | `DELETE /openmeter/features/{featureId}` | Delete a feature by id. |
| `client.features.queryCost` | `POST /openmeter/features/{featureId}/cost/query` | Query the cost of a feature as JSON. |
| `client.features.queryCostCsv` | `POST /openmeter/features/{featureId}/cost/query` | Download feature cost query results as CSV. Set `Accept: text/csv` for raw HTTP requests. In the TypeScript SDK, use `queryFeatureCostCsv` or `client.features.queryCostCsv`. In the Go SDK, use `Features.QueryCostCSV` or `Features.QueryCostCSVStream`. CSV columns, in order: `from, to, [subject,] [customer_id, customer_key, customer_name,] <dimensions...>, usage, cost, currency, detail` Subject and customer columns are included when the query groups by those dimensions, including grouping implied by their filters. Other dimensions follow the query's grouping order. Unavailable cost is an empty cell, not zero. Detail is retained for both unavailable and partially priced costs. Custom dimensions named `from`, `to`, `customer_key`, `customer_name`, `usage`, `cost`, `currency`, or `detail` are omitted from CSV headers and row values. Query grouping is unchanged, and JSON retains these dimension values. Timestamps preserve fractional seconds. |

### LLMCost

Expand Down
60 changes: 59 additions & 1 deletion api/spec/packages/aip-client-javascript/src/funcs/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import type {
DeleteFeatureResponse,
QueryFeatureCostRequest,
QueryFeatureCostResponse,
QueryFeatureCostCsvRequest,
QueryFeatureCostCsvResponse,
} from '../models/operations/features.js'

/**
Expand Down Expand Up @@ -215,7 +217,7 @@ export function deleteFeature(
/**
* Query feature cost
*
* Query the cost of a feature.
* Query the cost of a feature as JSON.
*
* POST /openmeter/features/{featureId}/cost/query
*/
Expand Down Expand Up @@ -255,3 +257,59 @@ export function queryFeatureCost(
})
})
}

/**
* Download feature cost query results as CSV.
*
* Set `Accept: text/csv` for raw HTTP requests. In the TypeScript SDK, use
* `queryFeatureCostCsv` or `client.features.queryCostCsv`. In the Go SDK, use
* `Features.QueryCostCSV` or `Features.QueryCostCSVStream`.
*
* CSV columns, in order:
*
* `from, to, [subject,] [customer_id, customer_key, customer_name,] <dimensions...>, usage, cost, currency, detail`
*
* Subject and customer columns are included when the query groups by those
* dimensions, including grouping implied by their filters. Other dimensions follow
* the query's grouping order. Unavailable cost is an empty cell, not zero. Detail
* is retained for both unavailable and partially priced costs.
*
* Custom dimensions named `from`, `to`, `customer_key`, `customer_name`, `usage`,
* `cost`, `currency`, or `detail` are omitted from CSV headers and row values.
* Query grouping is unchanged, and JSON retains these dimension values. Timestamps
* preserve fractional seconds.
*
* POST /openmeter/features/{featureId}/cost/query
*/
export function queryFeatureCostCsv(
client: Client,
req: QueryFeatureCostCsvRequest,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
options?: RequestOptions,
): Promise<Result<QueryFeatureCostCsvResponse>> {
const headers = new Headers(options?.headers as HeadersInit | undefined)
headers.set('accept', 'text/csv')
return request(() => {
const pathParamsInput = {
featureId: req.featureId,
}
const pathParams = client._options.validate
? toPathWire(pathParamsInput, schemas.queryFeatureCostCsvPathParams)
: pathParamsInput
if (client._options.validate) {
assertValid(schemas.queryFeatureCostCsvPathParamsWire, pathParams)
}
const path = `openmeter/features/${(() => {
if (pathParams.featureId === undefined) {
throw new Error('missing path parameter: featureId')
}
return encodeURIComponent(String(pathParams.featureId))
})()}/cost/query`
const body = toWire(req.body, schemas.queryFeatureCostCsvBody)
if (client._options.validate) {
assertValid(schemas.queryFeatureCostCsvBodyWire, body)
}
return http(client)
.post(path, { ...options, json: body, headers })
.text()
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,9 @@ export type QueryFeatureCostRequest = AcceptDateStrings<{
body: MeterQueryRequestInput
}>
export type QueryFeatureCostResponse = FeatureCostQueryResult

export type QueryFeatureCostCsvRequest = AcceptDateStrings<{
featureId: string
body: MeterQueryRequestInput
}>
export type QueryFeatureCostCsvResponse = string
16 changes: 16 additions & 0 deletions api/spec/packages/aip-client-javascript/src/models/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9426,6 +9426,14 @@ export const queryFeatureCostBody = meterQueryRequest

export const queryFeatureCostResponse = featureCostQueryResult

export const queryFeatureCostCsvPathParams = z.object({
featureId: ulid,
})

export const queryFeatureCostCsvBody = meterQueryRequest

export const queryFeatureCostCsvResponse = z.string()

export const listLlmCostPricesQueryParams = z.object({
filter: listLlmCostPricesParamsFilter.optional(),
sort: sortQuery.optional(),
Expand Down Expand Up @@ -19276,6 +19284,14 @@ export const queryFeatureCostBodyWire = meterQueryRequestWire

export const queryFeatureCostResponseWire = featureCostQueryResultWire

export const queryFeatureCostCsvPathParamsWire = z.object({
featureId: ulidWire,
})

export const queryFeatureCostCsvBodyWire = meterQueryRequestWire

export const queryFeatureCostCsvResponseWire = z.string()

export const listLlmCostPricesQueryParamsWire = z.object({
filter: listLlmCostPricesParamsFilterWire.optional(),
sort: z
Expand Down
35 changes: 34 additions & 1 deletion api/spec/packages/aip-client-javascript/src/sdk/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
updateFeature,
deleteFeature,
queryFeatureCost,
queryFeatureCostCsv,
} from '../funcs/features.js'
import type {
ListFeaturesRequest,
Expand All @@ -24,6 +25,8 @@ import type {
DeleteFeatureResponse,
QueryFeatureCostRequest,
QueryFeatureCostResponse,
QueryFeatureCostCsvRequest,
QueryFeatureCostCsvResponse,
} from '../models/operations/features.js'
import type { Feature } from '../models/types.js'

Expand Down Expand Up @@ -123,7 +126,7 @@ export class Features {
/**
* Query feature cost
*
* Query the cost of a feature.
* Query the cost of a feature as JSON.
*
* POST /openmeter/features/{featureId}/cost/query
*/
Expand All @@ -133,4 +136,34 @@ export class Features {
): Promise<QueryFeatureCostResponse> {
return unwrap(await queryFeatureCost(this._client, request, options))
}

/**
* Download feature cost query results as CSV.
*
* Set `Accept: text/csv` for raw HTTP requests. In the TypeScript SDK, use
* `queryFeatureCostCsv` or `client.features.queryCostCsv`. In the Go SDK, use
* `Features.QueryCostCSV` or `Features.QueryCostCSVStream`.
*
* CSV columns, in order:
*
* `from, to, [subject,] [customer_id, customer_key, customer_name,] <dimensions...>, usage, cost, currency, detail`
*
* Subject and customer columns are included when the query groups by those
* dimensions, including grouping implied by their filters. Other dimensions follow
* the query's grouping order. Unavailable cost is an empty cell, not zero. Detail
* is retained for both unavailable and partially priced costs.
*
* Custom dimensions named `from`, `to`, `customer_key`, `customer_name`, `usage`,
* `cost`, `currency`, or `detail` are omitted from CSV headers and row values.
* Query grouping is unchanged, and JSON retains these dimension values. Timestamps
* preserve fractional seconds.
*
* POST /openmeter/features/{featureId}/cost/query
*/
async queryCostCsv(
request: QueryFeatureCostCsvRequest,
options?: RequestOptions,
): Promise<QueryFeatureCostCsvResponse> {
return unwrap(await queryFeatureCostCsv(this._client, request, options))
}
}
Loading
Loading