@ancplua/qyl-api-schema 7.4.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/api/routes.tsp CHANGED
@@ -8,6 +8,7 @@ import "../otel/span.tsp";
8
8
  import "../otel/logs.tsp";
9
9
  import "../models/session.tsp";
10
10
  import "../models/health.tsp";
11
+ import "../models/metrics.tsp";
11
12
 
12
13
  using TypeSpec.Http;
13
14
  using TypeSpec.OpenAPI;
@@ -17,6 +18,7 @@ using Qyl.Api.Contracts.Common.Errors;
17
18
  using Qyl.Api.Contracts.Common.Pagination;
18
19
  using Qyl.Api.Contracts.OTel.Traces;
19
20
  using Qyl.Api.Contracts.OTel.Logs;
21
+ using Qyl.Api.Contracts.OTel.Metrics;
20
22
  using Qyl.Api.Contracts.OTel.Enums;
21
23
  using Qyl.Api.Contracts.Domains.Observe.Session;
22
24
  using Qyl.Api.Contracts.Health;
@@ -104,6 +106,113 @@ interface LogsApi {
104
106
  | InternalServerError;
105
107
  }
106
108
 
109
+ // -----------------------------------------------------------------------------
110
+ // Metrics
111
+ //
112
+ // Modelled on the Grafana/Prometheus range query — metric, label matchers,
113
+ // range, step — because the primary consumer is a coding agent over MCP. The
114
+ // collector aggregates server-side and returns buckets; raw points are never
115
+ // served, so a payload stays small no matter how dense the underlying stream.
116
+ // -----------------------------------------------------------------------------
117
+
118
+ @route("/api/v1/metrics")
119
+ @tag("Metrics")
120
+ @useAuth(QylApiKeyHeaderAuth)
121
+ interface MetricsApi {
122
+ @doc("List the metric names recorded for a project.")
123
+ @get
124
+ list(
125
+ ...ProjectScopeHeader,
126
+
127
+ @doc("Return only names starting with this prefix.")
128
+ @query("name_prefix")
129
+ namePrefix?: string,
130
+
131
+ @query
132
+ @minValue(1)
133
+ @maxValue(1000)
134
+ limit?: int32 = 200,
135
+ ):
136
+ | CursorPage<MetricDescriptor>
137
+ | ValidationError
138
+ | UnauthorizedError
139
+ | InternalServerError;
140
+
141
+ @doc("List the distinct attribute streams recorded under one metric name.")
142
+ @get
143
+ @route("/{metric_name}/series")
144
+ listSeries(
145
+ ...ProjectScopeHeader,
146
+ @path("metric_name") metricName: string,
147
+
148
+ @doc("""
149
+ Exact attribute match, repeatable, as `key=value`. A series must match
150
+ every matcher given.
151
+ """)
152
+ @query(#{ explode: true })
153
+ attr?: string[],
154
+
155
+ @doc("Attribute prefix match, repeatable, as `key=prefix`.")
156
+ @query(#{ name: "attr_prefix", explode: true })
157
+ attrPrefix?: string[],
158
+
159
+ @query
160
+ @minValue(1)
161
+ @maxValue(1000)
162
+ limit?: int32 = 200,
163
+ ):
164
+ | CursorPage<MetricSeries>
165
+ | ValidationError
166
+ | NotFoundError
167
+ | UnauthorizedError
168
+ | InternalServerError;
169
+
170
+ @doc("""
171
+ Aggregate one metric over a time range into buckets of `step_ms`.
172
+ Percentile aggregations require a histogram kind.
173
+ """)
174
+ @get
175
+ @route("/{metric_name}/query")
176
+ query(
177
+ ...ProjectScopeHeader,
178
+ @path("metric_name") metricName: string,
179
+
180
+ @query("start_time") startTime: utcDateTime,
181
+ @query("end_time") endTime: utcDateTime,
182
+
183
+ @doc("Bucket width in milliseconds. One bucket spanning the range collapses the query to a single value.")
184
+ @query("step_ms")
185
+ @minValue(1)
186
+ @maxValue(86400000)
187
+ stepMs?: int64 = 60000,
188
+
189
+ @query aggregation?: MetricAggregation = MetricAggregation.avg,
190
+
191
+ @doc("Attribute keys to split the result by, repeatable. Omitted, every matching series collapses into one stream.")
192
+ @query(#{ name: "group_by", explode: true })
193
+ groupBy?: string[],
194
+
195
+ @doc("Exact attribute match, repeatable, as `key=value`.")
196
+ @query(#{ explode: true })
197
+ attr?: string[],
198
+
199
+ @doc("Attribute prefix match, repeatable, as `key=prefix`.")
200
+ @query(#{ name: "attr_prefix", explode: true })
201
+ attrPrefix?: string[],
202
+
203
+ @doc("Maximum result streams. A grouping that exceeds this is truncated, not refused.")
204
+ @query("series_limit")
205
+ @minValue(1)
206
+ @maxValue(500)
207
+ seriesLimit?: int32 = 50,
208
+ ):
209
+ | MetricQueryResult
210
+ | ValidationError
211
+ | NotFoundError
212
+ | UnauthorizedError
213
+ | InternalServerError;
214
+ }
215
+
107
216
  // -----------------------------------------------------------------------------
108
217
  // Sessions
109
218
  // -----------------------------------------------------------------------------