Skip to content

Commit cd53796

Browse files
authored
feat(api): meter query v3 (#3918)
1 parent 6faf0bc commit cd53796

File tree

15 files changed

+1230
-471
lines changed

15 files changed

+1230
-471
lines changed

api/spec/lib/rules/field-prefix.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
import { createRule, paramMessage } from '@typespec/compiler'
22

3+
// List of prefixes that are excluded from the rule.
4+
// These prefixes are defined in the AIP.
5+
const EXCLUDED_PREFIXES = [
6+
'allow',
7+
'custom',
8+
'default',
9+
'disable',
10+
'enable',
11+
'include_in',
12+
'initial',
13+
'is',
14+
'last',
15+
'primary',
16+
]
17+
318
export const repeatedPrefixGroupingRule = createRule({
419
name: 'repeated-prefix-grouping',
520
severity: 'warning',
@@ -35,6 +50,10 @@ export const repeatedPrefixGroupingRule = createRule({
3550
}
3651

3752
for (const [prefix, fields] of byPrefix.entries()) {
53+
if (EXCLUDED_PREFIXES.includes(prefix)) {
54+
continue
55+
}
56+
3857
if (fields.length <= 1) {
3958
continue
4059
}

api/spec/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@typespec/compiler": "1.9.0",
3232
"@typespec/http": "1.9.1",
3333
"@typespec/http-client-python": "0.27.2",
34+
"@typespec/json-schema": "1.9.0",
3435
"@typespec/openapi": "1.9.0",
3536
"@typespec/openapi3": "1.9.0",
3637
"@typespec/prettier-plugin-typespec": "1.9.0",

api/spec/pnpm-lock.yaml

Lines changed: 18 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/spec/src/v3/apps/external_invoicing.tsp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ namespace Apps;
1919
* State synchronization is controlled by hooks that pause invoice progression until the
2020
* external system confirms synchronization via API callbacks.
2121
*/
22-
#suppress "@openmeter/api-spec/repeated-prefix-grouping" "Hooks are conceptually separate settings"
2322
@friendlyName("BillingAppExternalInvoicing")
2423
model AppExternalInvoicing {
2524
...AppBase<AppType.ExternalInvoicing>;

api/spec/src/v3/currencies/cost-bases/operations.tsp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ interface CurrenciesCustomCostBasesOperations {
3131
*/
3232
@extension(Shared.PrivateExtension, true)
3333
@extension(Shared.UnstableExtension, true)
34+
@extension(Shared.InternalExtension, true)
3435
@get
3536
@operationId("list-cost-bases")
3637
@summary("List cost bases")
@@ -53,6 +54,7 @@ interface CurrenciesCustomCostBasesOperations {
5354
*/
5455
@extension(Shared.PrivateExtension, true)
5556
@extension(Shared.UnstableExtension, true)
57+
@extension(Shared.InternalExtension, true)
5658
@post
5759
@operationId("create-cost-basis")
5860
@summary("Create cost basis")

api/spec/src/v3/currencies/operations.tsp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ interface CurrenciesOperations {
2929
*/
3030
@extension(Shared.PrivateExtension, true)
3131
@extension(Shared.UnstableExtension, true)
32+
@extension(Shared.InternalExtension, true)
3233
@get
3334
@operationId("list-currencies")
3435
@summary("List currencies")
@@ -52,6 +53,7 @@ interface CurrenciesCustomOperations {
5253
*/
5354
@extension(Shared.PrivateExtension, true)
5455
@extension(Shared.UnstableExtension, true)
56+
@extension(Shared.InternalExtension, true)
5557
@post
5658
@operationId("create-custom-currency")
5759
@summary("Create custom currency")

api/spec/src/v3/customers/billing.tsp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ namespace Customers;
66
/**
77
* Billing customer data.
88
*/
9-
#suppress "@openmeter/api-spec/repeated-prefix-grouping" "Read and write properties"
109
@friendlyName("BillingCustomerData")
1110
model CustomerBillingData {
1211
/**

api/spec/src/v3/llmcost/operations.tsp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ interface LLMCostPricesOperations {
6666
*/
6767
@extension(Shared.PrivateExtension, true)
6868
@extension(Shared.UnstableExtension, true)
69+
@extension(Shared.InternalExtension, true)
6970
@get
7071
@operationId("list-llm-cost-prices")
7172
@summary("List LLM cost prices")
@@ -85,6 +86,7 @@ interface LLMCostPricesOperations {
8586
*/
8687
@extension(Shared.PrivateExtension, true)
8788
@extension(Shared.UnstableExtension, true)
89+
@extension(Shared.InternalExtension, true)
8890
@get
8991
@operationId("get-llm-cost-price")
9092
@summary("Get LLM cost price")
@@ -102,6 +104,7 @@ interface LLMCostOverridesOperations {
102104
*/
103105
@extension(Shared.PrivateExtension, true)
104106
@extension(Shared.UnstableExtension, true)
107+
@extension(Shared.InternalExtension, true)
105108
@get
106109
@operationId("list-llm-cost-overrides")
107110
@summary("List LLM cost overrides")
@@ -117,6 +120,7 @@ interface LLMCostOverridesOperations {
117120
*/
118121
@extension(Shared.PrivateExtension, true)
119122
@extension(Shared.UnstableExtension, true)
123+
@extension(Shared.InternalExtension, true)
120124
@post
121125
@operationId("create-llm-cost-override")
122126
@summary("Create LLM cost override")
@@ -129,6 +133,7 @@ interface LLMCostOverridesOperations {
129133
*/
130134
@extension(Shared.PrivateExtension, true)
131135
@extension(Shared.UnstableExtension, true)
136+
@extension(Shared.InternalExtension, true)
132137
@delete
133138
@operationId("delete-llm-cost-override")
134139
@summary("Delete LLM cost override")

api/spec/src/v3/meters/operations.tsp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import "../common/pagination.tsp";
77
import "../common/parameters.tsp";
88
import "../shared/index.tsp";
99
import "./meter.tsp";
10+
import "./query.tsp";
1011

1112
using TypeSpec.Http;
1213
using TypeSpec.OpenAPI;
@@ -53,4 +54,18 @@ interface MetersOperations {
5354
delete(
5455
@path meterId: Shared.ULID,
5556
): Shared.DeleteResponse | Common.NotFound | Common.ErrorResponses;
57+
58+
/**
59+
* Query a meter for usage.
60+
*/
61+
@extension(Shared.PrivateExtension, true)
62+
@extension(Shared.UnstableExtension, true)
63+
@extension(Shared.InternalExtension, true)
64+
@post
65+
@operationId("query-meter")
66+
@summary("Query meter")
67+
query(
68+
@path meterId: Shared.ULID,
69+
@body request: MeterQueryRequest,
70+
): MeterQueryResult | Common.NotFound | Common.ErrorResponses;
5671
}

api/spec/src/v3/meters/query.tsp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import "@typespec/json-schema";
2+
import "../shared/index.tsp";
3+
4+
namespace Meters;
5+
6+
/**
7+
* The granularity of the time grouping.
8+
* Time durations are specified in ISO 8601 format.
9+
*/
10+
@friendlyName("MeterQueryGranularity")
11+
union MeterQueryGranularity {
12+
/**
13+
* 1 minute time buckets.
14+
*/
15+
@summary("1 minute")
16+
Minute: "PT1M",
17+
18+
/**
19+
* 1 hour time buckets.
20+
*/
21+
@summary("1 hour")
22+
Hour: "PT1H",
23+
24+
/**
25+
* 1 day time buckets.
26+
*/
27+
@summary("1 day")
28+
Day: "P1D",
29+
30+
/**
31+
* 1 month time buckets.
32+
*/
33+
@summary("1 month")
34+
Month: "P1M",
35+
}
36+
37+
/**
38+
* Filters to apply to a meter query.
39+
*/
40+
@friendlyName("MeterQueryFilters")
41+
model MeterQueryFilters {
42+
/**
43+
* Filters to apply to the subject key of the query.
44+
* Only equals ("eq", "in") comparisons are supported.
45+
*/
46+
subject_key?: Shared.QueryFilterString;
47+
48+
/**
49+
* Filters to apply to the customer ID of the query.
50+
* Only equals ("eq", "in") comparisons are supported.
51+
*/
52+
customer_id?: Shared.QueryFilterString;
53+
54+
/**
55+
* Filters to apply to the dimensions of the query.
56+
*/
57+
@JsonSchema.maxProperties(10)
58+
dimensions?: Shared.QueryFilterStringMap;
59+
}
60+
61+
/**
62+
* A meter query request.
63+
*/
64+
#suppress "@openmeter/api-spec/repeated-prefix-grouping" "keep the request flat"
65+
@friendlyName("MeterQueryRequest")
66+
@example(#{
67+
from: Shared.DateTime.fromISO("2023-01-01T00:00:00Z"),
68+
to: Shared.DateTime.fromISO("2023-01-02T00:00:00Z"),
69+
granularity: MeterQueryGranularity.Day,
70+
time_zone: "UTC",
71+
})
72+
model MeterQueryRequest {
73+
/**
74+
* The start of the period the usage is queried from.
75+
*/
76+
from?: Shared.DateTime;
77+
78+
/**
79+
* The end of the period the usage is queried to.
80+
*/
81+
to?: Shared.DateTime;
82+
83+
/**
84+
* The size of the time buckets to group the usage into.
85+
* If not specified, the usage is aggregated over the entire period.
86+
*/
87+
granularity?: MeterQueryGranularity;
88+
89+
/**
90+
* The value is the name of the time zone as defined in the IANA Time Zone Database (http://www.iana.org/time-zones).
91+
* The time zone is used to determine the start and end of the time buckets.
92+
* If not specified, the UTC timezone will be used.
93+
*/
94+
time_zone?: string = "UTC";
95+
96+
/**
97+
* The dimensions to group the results by.
98+
*/
99+
@example(#["model", "type"])
100+
@maxItems(100)
101+
group_by_dimensions?: string[];
102+
103+
/**
104+
* Filters to apply to the query.
105+
*/
106+
filters?: MeterQueryFilters;
107+
}
108+
109+
/**
110+
* Meter query result.
111+
*/
112+
@friendlyName("MeterQueryResult")
113+
@example(#{
114+
from: Shared.DateTime.fromISO("2023-01-01T00:00:00Z"),
115+
to: Shared.DateTime.fromISO("2023-01-02T00:00:00Z"),
116+
data: #[
117+
#{
118+
value: "12.3456",
119+
from: Shared.DateTime.fromISO("2023-01-01T00:00:00Z"),
120+
to: Shared.DateTime.fromISO("2023-01-02T00:00:00Z"),
121+
subject_key: "83d489bd-13a5-4a3d-a886-44c385b5c43b",
122+
dimensions: #{ `model`: "gpt-4-turbo", type: "input" },
123+
}
124+
],
125+
})
126+
model MeterQueryResult {
127+
/**
128+
* The start of the period the usage is queried from.
129+
*/
130+
from?: Shared.DateTime;
131+
132+
/**
133+
* The end of the period the usage is queried to.
134+
*/
135+
to?: Shared.DateTime;
136+
137+
/**
138+
* The usage data.
139+
* If no data is available, an empty array is returned.
140+
*/
141+
data: MeterQueryRow[];
142+
}
143+
144+
/**
145+
* A row in the result of a meter query.
146+
*/
147+
#suppress "@openmeter/api-spec/repeated-prefix-grouping" "keep the row flat"
148+
@friendlyName("MeterQueryRow")
149+
@example(#{
150+
value: "12.3456",
151+
from: Shared.DateTime.fromISO("2023-01-01T00:00:00Z"),
152+
to: Shared.DateTime.fromISO("2023-01-02T00:00:00Z"),
153+
subject_key: "83d489bd-13a5-4a3d-a886-44c385b5c43b",
154+
dimensions: #{ `model`: "gpt-4-turbo", type: "input" },
155+
})
156+
model MeterQueryRow {
157+
/**
158+
* The aggregated value.
159+
*/
160+
value: Shared.Numeric;
161+
162+
/**
163+
* The start of the time bucket the value is aggregated over.
164+
*/
165+
from: Shared.DateTime;
166+
167+
/**
168+
* The end of the time bucket the value is aggregated over.
169+
*/
170+
to: Shared.DateTime;
171+
172+
/**
173+
* The subject key the value is aggregated over.
174+
* If not specified, the value is aggregated over all subjects.
175+
*/
176+
subject_key?: string;
177+
178+
/**
179+
* The customer ID the value is aggregated over.
180+
*/
181+
customer_id?: string;
182+
183+
/**
184+
* The dimensions the value is aggregated over.
185+
*/
186+
dimensions: Record<string | null>;
187+
}

0 commit comments

Comments
 (0)