@mcoda/agents 0.1.40 → 0.1.41

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.
@@ -1 +1 @@
1
- {"version":3,"file":"OpenAiAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAwHtG,KAAK,YAAY,GAAG,aAAa,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,YAAW,YAAY;IAMpC,OAAO,CAAC,MAAM;IAL1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,SAAS,CAAsC;gBAEnC,MAAM,EAAE,YAAY;IAUlC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAgFnC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwC5D,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC;IAwFhG,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;CAU7B"}
1
+ {"version":3,"file":"OpenAiAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAiKtG,KAAK,YAAY,GAAG,aAAa,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,YAAW,YAAY;IAMpC,OAAO,CAAC,MAAM;IAL1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,SAAS,CAAsC;gBAEnC,MAAM,EAAE,YAAY;IAUlC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IA6GnC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwC5D,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC;IAwFhG,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;CAU7B"}
@@ -1,4 +1,13 @@
1
+ import { parseUsageLimitError } from "../../AgentService/UsageLimitParser.js";
1
2
  const DEFAULT_BASE_URL = "https://api.openai.com/v1";
3
+ const MAX_RESPONSE_DETAIL_CHARS = 500;
4
+ const RATE_LIMIT_HEADER_NAMES = [
5
+ "retry-after",
6
+ "x-ratelimit-reset-after",
7
+ "x-ratelimit-reset",
8
+ "x-ratelimit-reset-at",
9
+ "x-ratelimit-remaining",
10
+ ];
2
11
  const asString = (value) => (typeof value === "string" ? value : undefined);
3
12
  const resolveString = (value) => {
4
13
  const raw = asString(value)?.trim();
@@ -11,6 +20,37 @@ const normalizeBaseUrl = (value) => {
11
20
  return undefined;
12
21
  return str.endsWith("/") ? str.slice(0, -1) : str;
13
22
  };
23
+ const buildRateLimitProbeMessage = (response, responseText) => {
24
+ const parts = [`openai_probe http ${response.status}`];
25
+ const retryAfter = response.headers.get("retry-after")?.trim();
26
+ if (retryAfter) {
27
+ const retryAfterSeconds = Number.parseInt(retryAfter, 10);
28
+ parts.push(Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0
29
+ ? `Retry after ${retryAfterSeconds} seconds`
30
+ : `Retry after ${retryAfter}`);
31
+ }
32
+ for (const headerName of RATE_LIMIT_HEADER_NAMES) {
33
+ if (headerName === "retry-after")
34
+ continue;
35
+ const headerValue = response.headers.get(headerName)?.trim();
36
+ if (headerValue) {
37
+ parts.push(`${headerName}: ${headerValue}`);
38
+ }
39
+ }
40
+ const trimmedResponse = responseText.trim();
41
+ if (trimmedResponse) {
42
+ parts.push(trimmedResponse);
43
+ }
44
+ return parts.join(". ");
45
+ };
46
+ const resolveRetryAfterMs = (resetAt, nowMs) => {
47
+ if (!resetAt)
48
+ return undefined;
49
+ const timestampMs = Date.parse(resetAt);
50
+ if (!Number.isFinite(timestampMs))
51
+ return undefined;
52
+ return Math.max(0, timestampMs - nowMs);
53
+ };
14
54
  const resolveBaseUrl = (config) => {
15
55
  const anyConfig = config;
16
56
  const agentConfig = config.agent?.config;
@@ -148,12 +188,38 @@ export class OpenAiAdapter {
148
188
  body: JSON.stringify(this.buildHealthCheckBody(model)),
149
189
  });
150
190
  const responseText = await response.text().catch(() => "");
151
- const latencyMs = Date.now() - startedAt;
191
+ const checkedAtMs = Date.now();
192
+ const lastCheckedAt = new Date(checkedAtMs).toISOString();
193
+ const latencyMs = checkedAtMs - startedAt;
152
194
  if (!response.ok) {
195
+ if (response.status === 429) {
196
+ const parsedLimit = parseUsageLimitError(new Error(buildRateLimitProbeMessage(response, responseText)), checkedAtMs);
197
+ return {
198
+ agentId: this.config.agent.id,
199
+ status: "healthy",
200
+ lastCheckedAt,
201
+ latencyMs,
202
+ details: {
203
+ adapter: "openai-api",
204
+ source: "openai_probe",
205
+ model,
206
+ baseUrl: url,
207
+ reason: "rate_limited",
208
+ transient: true,
209
+ rateLimited: true,
210
+ httpStatus: response.status,
211
+ response: responseText.slice(0, MAX_RESPONSE_DETAIL_CHARS),
212
+ resetAt: parsedLimit?.resetAt,
213
+ resetAtSource: parsedLimit?.resetAtSource,
214
+ retryAfterMs: resolveRetryAfterMs(parsedLimit?.resetAt, checkedAtMs),
215
+ windowTypes: parsedLimit?.windowTypes,
216
+ },
217
+ };
218
+ }
153
219
  return {
154
220
  agentId: this.config.agent.id,
155
- status: response.status === 429 ? "degraded" : "unreachable",
156
- lastCheckedAt: new Date().toISOString(),
221
+ status: "unreachable",
222
+ lastCheckedAt,
157
223
  latencyMs,
158
224
  details: {
159
225
  adapter: "openai-api",
@@ -162,14 +228,14 @@ export class OpenAiAdapter {
162
228
  baseUrl: url,
163
229
  reason: "http_error",
164
230
  httpStatus: response.status,
165
- response: responseText.slice(0, 500),
231
+ response: responseText.slice(0, MAX_RESPONSE_DETAIL_CHARS),
166
232
  },
167
233
  };
168
234
  }
169
235
  return {
170
236
  agentId: this.config.agent.id,
171
237
  status: "healthy",
172
- lastCheckedAt: new Date().toISOString(),
238
+ lastCheckedAt,
173
239
  latencyMs,
174
240
  details: {
175
241
  adapter: "openai-api",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcoda/agents",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "description": "Agent registry and capabilities for mcoda.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@mcoda/shared": "0.1.40",
34
- "@mcoda/db": "0.1.40"
33
+ "@mcoda/shared": "0.1.41",
34
+ "@mcoda/db": "0.1.41"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsc -p tsconfig.json",