@jiayunxie/aerial 0.1.7 → 0.1.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/copilot.js +56 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiayunxie/aerial",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Local-only GitHub Copilot proxy for Codex CLI and Claude Code.",
5
5
  "type": "module",
6
6
  "private": false,
package/src/copilot.js CHANGED
@@ -174,18 +174,52 @@ function withDefaultAnthropicCache(payload) {
174
174
  return next;
175
175
  }
176
176
 
177
- function withSupportedAnthropicEffort(payload) {
177
+ function canonicalClaudeOpus47Family(model) {
178
+ return /^claude-opus-4[.-]7(?:-|$)/.test(model) ? "claude-opus-4.7" : undefined;
179
+ }
180
+
181
+ function modelHasMessagesRoute(model) {
182
+ const endpoints = Array.isArray(model?.supported_endpoints) ? model.supported_endpoints : [];
183
+ const routes = Array.isArray(model?.aerial?.routes) ? model.aerial.routes : [];
184
+ return endpoints.includes("/v1/messages") || routes.includes("messages");
185
+ }
186
+
187
+ function supportedReasoningEfforts(model) {
188
+ const supports = model?.capabilities?.supports;
189
+ const values = supports?.reasoning_effort ?? supports?.reasoning_efforts;
190
+ if (Array.isArray(values)) return values.map(String);
191
+ if (typeof values === "string") return [values];
192
+ return [];
193
+ }
194
+
195
+ function modelSupportsAdaptiveThinking(model) {
196
+ const supports = model?.capabilities?.supports;
197
+ return supports?.adaptive_thinking === true || supports?.thinking?.adaptive === true;
198
+ }
199
+
200
+ function findAnthropicEffortModel(modelId, effort, models) {
201
+ if (!Array.isArray(models)) return undefined;
202
+ const family = canonicalClaudeOpus47Family(modelId);
203
+ if (!family) return undefined;
204
+ const candidates = models.filter((model) => {
205
+ const id = typeof model?.id === "string" ? model.id : "";
206
+ return canonicalClaudeOpus47Family(id) === family &&
207
+ modelHasMessagesRoute(model) &&
208
+ modelSupportsAdaptiveThinking(model) &&
209
+ supportedReasoningEfforts(model).includes(effort);
210
+ });
211
+ return candidates.find((model) => model.id === modelId) || candidates[0];
212
+ }
213
+
214
+ function withSupportedAnthropicEffort(payload, models) {
178
215
  const effort = payload?.output_config?.effort;
179
216
  if (effort === undefined) return payload;
180
217
  const model = typeof payload?.model === "string" ? payload.model : "";
181
- if (!/^claude-opus-4[.-]7(?:-|$)/.test(model)) return payload;
218
+ if (!canonicalClaudeOpus47Family(model)) return payload;
182
219
  const nextEffort = effort === "max" ? "xhigh" : effort;
183
220
  if (!["low", "medium", "high", "xhigh"].includes(nextEffort)) return payload;
184
- const baseModel = /^claude-opus-4[.-]7$/.test(model);
185
- const legacyEffortModel = /^claude-opus-4[.-]7-(?:high|xhigh)$/.test(model);
186
- const nextModel = legacyEffortModel || (baseModel && nextEffort !== "medium")
187
- ? "claude-opus-4.7-1m-internal"
188
- : model;
221
+ const routed = findAnthropicEffortModel(model, nextEffort, models);
222
+ const nextModel = routed?.id || model;
189
223
  if (model === nextModel && effort === nextEffort) return payload;
190
224
  logEvent("anthropic_effort_route", { model, effort, routedModel: nextModel, routedEffort: nextEffort });
191
225
  return { ...payload, model: nextModel, output_config: { ...payload.output_config, effort: nextEffort } };
@@ -221,8 +255,18 @@ function withSupportedAnthropicThinking(payload) {
221
255
  };
222
256
  }
223
257
 
224
- function withAnthropicDefaults(payload) {
225
- return withSupportedAnthropicEffort(withSupportedAnthropicThinking(withDefaultAnthropicCache(payload)));
258
+ function shouldLoadAnthropicCatalog(payload) {
259
+ const effort = payload?.output_config?.effort;
260
+ const model = typeof payload?.model === "string" ? payload.model : "";
261
+ return effort !== undefined && Boolean(canonicalClaudeOpus47Family(model));
262
+ }
263
+
264
+ async function withAnthropicDefaults(payload) {
265
+ const next = withSupportedAnthropicThinking(withDefaultAnthropicCache(payload));
266
+ const models = shouldLoadAnthropicCatalog(next)
267
+ ? await fetchModelsCatalog().catch(() => undefined)
268
+ : undefined;
269
+ return withSupportedAnthropicEffort(next, models);
226
270
  }
227
271
 
228
272
  function parseJsonBody(body, contentType) {
@@ -407,7 +451,7 @@ function wrapResponseWithCacheObserver(upstream, route, requestCache) {
407
451
 
408
452
  async function requestWithJsonBody(request, transform) {
409
453
  const payload = await request.json();
410
- const nextPayload = transform(payload);
454
+ const nextPayload = await transform(payload);
411
455
  return new Request(request.url, {
412
456
  method: request.method,
413
457
  headers: request.headers,
@@ -435,7 +479,7 @@ async function proxyFetch(path, request, { extraHeaders = {}, bodyOverride } = {
435
479
  return wrapResponseWithCacheObserver(upstream, path, requestCache);
436
480
  }
437
481
 
438
- async function fetchModelsForResponseSelection() {
482
+ async function fetchModelsCatalog() {
439
483
  const token = await getCopilotToken();
440
484
  const response = await fetch(`${COPILOT_API_ORIGIN}/models`, { method: "GET", headers: upstreamHeaders(token) });
441
485
  if (!response.ok) return undefined;
@@ -461,7 +505,7 @@ export async function proxyResponses(request) {
461
505
  // per-request /models lookup to avoid an extra upstream call and let the
462
506
  // shared HTTP path handle cache_request logging via proxyFetch.
463
507
  if (payload?.stream && isResponsesWebSocketOptIn()) {
464
- const models = await fetchModelsForResponseSelection().catch(() => undefined);
508
+ const models = await fetchModelsCatalog().catch(() => undefined);
465
509
  const model = models ? responseModelFromCatalog(payload.model, models) : undefined;
466
510
  if (shouldUseResponsesWebSocket(payload, model)) {
467
511
  const requestCache = cacheRequestFields(payload);