@standardagents/openrouter 0.24.5 → 0.24.7
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/dist/index.d.ts +21 -0
- package/dist/index.js +110 -14
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -173,11 +173,32 @@ declare class OpenRouterProvider implements LLMProviderInterface {
|
|
|
173
173
|
private static modelsCache;
|
|
174
174
|
private static modelsCacheTime;
|
|
175
175
|
private static readonly CACHE_TTL;
|
|
176
|
+
/** In-flight catalog download shared by concurrent callers. */
|
|
177
|
+
private static modelsFetchPromise;
|
|
178
|
+
/** Timestamp of the last failed catalog fetch, for negative caching. */
|
|
179
|
+
private static modelsFetchFailedAt;
|
|
180
|
+
private static readonly FAILURE_RETRY_MS;
|
|
176
181
|
/**
|
|
177
182
|
* Get capabilities for a specific model.
|
|
178
183
|
* Fetches from OpenRouter's models API which provides rich metadata.
|
|
179
184
|
*/
|
|
180
185
|
getModelCapabilities(modelId: string): Promise<ModelCapabilities | null>;
|
|
186
|
+
/**
|
|
187
|
+
* How long a streaming finish chunk may wait on the catalog pricing lookup.
|
|
188
|
+
* The lookup is started when the stream starts, so by finish time it is
|
|
189
|
+
* almost always resolved; the bound guarantees a slow catalog download can
|
|
190
|
+
* never stall the finish chunk into the runtime's stream watchdog.
|
|
191
|
+
*/
|
|
192
|
+
private static readonly USAGE_PRICING_FINISH_WAIT_MS;
|
|
193
|
+
private static settleUsagePricing;
|
|
194
|
+
/** Bounded-wait pricing attachment shared by the non-streaming paths. */
|
|
195
|
+
private attachUsagePricing;
|
|
196
|
+
/**
|
|
197
|
+
* Exact unit prices for a model from OpenRouter's live catalog (cached),
|
|
198
|
+
* attached to response usage so request logs can record specific prices.
|
|
199
|
+
* Best-effort: any failure just leaves pricing off the usage payload.
|
|
200
|
+
*/
|
|
201
|
+
private getUsagePricing;
|
|
181
202
|
private fetchModelsWithCache;
|
|
182
203
|
private mapOpenRouterCapabilities;
|
|
183
204
|
/**
|
package/dist/index.js
CHANGED
|
@@ -214,11 +214,17 @@ function transformChatAssistantMessage(msg) {
|
|
|
214
214
|
}));
|
|
215
215
|
}
|
|
216
216
|
if (msg.reasoningDetails && msg.reasoningDetails.length > 0) {
|
|
217
|
-
const details = msg.reasoningDetails.filter((detail) =>
|
|
218
|
-
|
|
217
|
+
const details = msg.reasoningDetails.filter((detail) => {
|
|
218
|
+
const type = detail.type;
|
|
219
|
+
if (type !== "encrypted" && type !== "reasoning.encrypted") return true;
|
|
220
|
+
return typeof detail.data === "string" && detail.data.length > 0;
|
|
221
|
+
}).map((detail) => {
|
|
222
|
+
const type = detail.type;
|
|
223
|
+
const legacy = detail;
|
|
224
|
+
if (type === "text" || type === "reasoning.text") {
|
|
219
225
|
return { type: "reasoning.text", text: detail.text, format: detail.format };
|
|
220
|
-
} else if (
|
|
221
|
-
return { type: "reasoning.summary", summary: detail.text, format: detail.format };
|
|
226
|
+
} else if (type === "summary" || type === "reasoning.summary") {
|
|
227
|
+
return { type: "reasoning.summary", summary: detail.text ?? legacy.summary, format: detail.format };
|
|
222
228
|
}
|
|
223
229
|
return { type: "reasoning.encrypted", data: detail.data, id: detail.id, format: detail.format };
|
|
224
230
|
});
|
|
@@ -1944,6 +1950,25 @@ function getModelIconDataUri(modelId) {
|
|
|
1944
1950
|
}
|
|
1945
1951
|
|
|
1946
1952
|
// src/OpenRouterProvider.ts
|
|
1953
|
+
function usagePricingFromCatalogPricing(pricing) {
|
|
1954
|
+
if (!pricing) return void 0;
|
|
1955
|
+
const perMillion = (value) => {
|
|
1956
|
+
if (value === void 0) return void 0;
|
|
1957
|
+
const parsed = Number.parseFloat(value);
|
|
1958
|
+
if (!Number.isFinite(parsed)) return void 0;
|
|
1959
|
+
return Number((parsed * 1e6).toFixed(12));
|
|
1960
|
+
};
|
|
1961
|
+
const inputPerMillion = perMillion(pricing.prompt);
|
|
1962
|
+
const outputPerMillion = perMillion(pricing.completion);
|
|
1963
|
+
if (inputPerMillion === void 0 || outputPerMillion === void 0) {
|
|
1964
|
+
return void 0;
|
|
1965
|
+
}
|
|
1966
|
+
return {
|
|
1967
|
+
inputPerMillion,
|
|
1968
|
+
cachedInputPerMillion: perMillion(pricing.input_cache_read),
|
|
1969
|
+
outputPerMillion
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1947
1972
|
var OpenRouterProvider = class _OpenRouterProvider {
|
|
1948
1973
|
name = "openrouter";
|
|
1949
1974
|
specificationVersion = "1";
|
|
@@ -2075,6 +2100,11 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2075
2100
|
static modelsCacheTime = 0;
|
|
2076
2101
|
static CACHE_TTL = 5 * 60 * 1e3;
|
|
2077
2102
|
// 5 minutes
|
|
2103
|
+
/** In-flight catalog download shared by concurrent callers. */
|
|
2104
|
+
static modelsFetchPromise = null;
|
|
2105
|
+
/** Timestamp of the last failed catalog fetch, for negative caching. */
|
|
2106
|
+
static modelsFetchFailedAt = 0;
|
|
2107
|
+
static FAILURE_RETRY_MS = 60 * 1e3;
|
|
2078
2108
|
/**
|
|
2079
2109
|
* Get capabilities for a specific model.
|
|
2080
2110
|
* Fetches from OpenRouter's models API which provides rich metadata.
|
|
@@ -2092,19 +2122,73 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2092
2122
|
return null;
|
|
2093
2123
|
}
|
|
2094
2124
|
}
|
|
2125
|
+
/**
|
|
2126
|
+
* How long a streaming finish chunk may wait on the catalog pricing lookup.
|
|
2127
|
+
* The lookup is started when the stream starts, so by finish time it is
|
|
2128
|
+
* almost always resolved; the bound guarantees a slow catalog download can
|
|
2129
|
+
* never stall the finish chunk into the runtime's stream watchdog.
|
|
2130
|
+
*/
|
|
2131
|
+
static USAGE_PRICING_FINISH_WAIT_MS = 2500;
|
|
2132
|
+
static async settleUsagePricing(pricingPromise) {
|
|
2133
|
+
let timer;
|
|
2134
|
+
const timeout = new Promise((resolve) => {
|
|
2135
|
+
timer = setTimeout(() => resolve(void 0), _OpenRouterProvider.USAGE_PRICING_FINISH_WAIT_MS);
|
|
2136
|
+
});
|
|
2137
|
+
try {
|
|
2138
|
+
return await Promise.race([pricingPromise, timeout]);
|
|
2139
|
+
} finally {
|
|
2140
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
/** Bounded-wait pricing attachment shared by the non-streaming paths. */
|
|
2144
|
+
async attachUsagePricing(result, pricingPromise) {
|
|
2145
|
+
const pricing = await _OpenRouterProvider.settleUsagePricing(pricingPromise);
|
|
2146
|
+
if (pricing) result.usage.pricing = pricing;
|
|
2147
|
+
return result;
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Exact unit prices for a model from OpenRouter's live catalog (cached),
|
|
2151
|
+
* attached to response usage so request logs can record specific prices.
|
|
2152
|
+
* Best-effort: any failure just leaves pricing off the usage payload.
|
|
2153
|
+
*/
|
|
2154
|
+
async getUsagePricing(modelId) {
|
|
2155
|
+
if (!modelId) return void 0;
|
|
2156
|
+
try {
|
|
2157
|
+
const models = await this.fetchModelsWithCache();
|
|
2158
|
+
const model = models.find((m) => m.id === modelId);
|
|
2159
|
+
return usagePricingFromCatalogPricing(model?.pricing);
|
|
2160
|
+
} catch {
|
|
2161
|
+
return void 0;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2095
2164
|
async fetchModelsWithCache() {
|
|
2096
2165
|
const now = Date.now();
|
|
2097
2166
|
if (_OpenRouterProvider.modelsCache && now - _OpenRouterProvider.modelsCacheTime < _OpenRouterProvider.CACHE_TTL) {
|
|
2098
2167
|
return _OpenRouterProvider.modelsCache;
|
|
2099
2168
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
throw new Error(`OpenRouter API error: ${response.status}`);
|
|
2169
|
+
if (now - _OpenRouterProvider.modelsFetchFailedAt < _OpenRouterProvider.FAILURE_RETRY_MS) {
|
|
2170
|
+
throw new Error("OpenRouter models fetch recently failed");
|
|
2103
2171
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2172
|
+
if (!_OpenRouterProvider.modelsFetchPromise) {
|
|
2173
|
+
_OpenRouterProvider.modelsFetchPromise = (async () => {
|
|
2174
|
+
try {
|
|
2175
|
+
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
2176
|
+
if (!response.ok) {
|
|
2177
|
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
|
2178
|
+
}
|
|
2179
|
+
const data = await response.json();
|
|
2180
|
+
_OpenRouterProvider.modelsCache = data.data || [];
|
|
2181
|
+
_OpenRouterProvider.modelsCacheTime = Date.now();
|
|
2182
|
+
return _OpenRouterProvider.modelsCache;
|
|
2183
|
+
} catch (error) {
|
|
2184
|
+
_OpenRouterProvider.modelsFetchFailedAt = Date.now();
|
|
2185
|
+
throw error;
|
|
2186
|
+
} finally {
|
|
2187
|
+
_OpenRouterProvider.modelsFetchPromise = null;
|
|
2188
|
+
}
|
|
2189
|
+
})();
|
|
2190
|
+
}
|
|
2191
|
+
return _OpenRouterProvider.modelsFetchPromise;
|
|
2108
2192
|
}
|
|
2109
2193
|
mapOpenRouterCapabilities(model) {
|
|
2110
2194
|
const capabilities = {
|
|
@@ -2189,6 +2273,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2189
2273
|
if (this.config.providers && this.config.providers.length > 0) {
|
|
2190
2274
|
params.provider = { only: this.config.providers };
|
|
2191
2275
|
}
|
|
2276
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2192
2277
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
2193
2278
|
method: "POST",
|
|
2194
2279
|
headers: {
|
|
@@ -2210,7 +2295,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2210
2295
|
throw new ProviderError(errorMessage, "invalid_request", response.status);
|
|
2211
2296
|
}
|
|
2212
2297
|
const data = await response.json();
|
|
2213
|
-
return transformChatResponse(data);
|
|
2298
|
+
return this.attachUsagePricing(transformChatResponse(data), pricingPromise);
|
|
2214
2299
|
} catch (error) {
|
|
2215
2300
|
throw this.toProviderError(error);
|
|
2216
2301
|
}
|
|
@@ -2224,6 +2309,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2224
2309
|
try {
|
|
2225
2310
|
const params = buildCreateParams(request);
|
|
2226
2311
|
this.applyConfiguredProvidersToResponses(params);
|
|
2312
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2227
2313
|
if (this.hasOpenRouterServerTools(params)) {
|
|
2228
2314
|
const response2 = await fetch(`${baseUrl}/responses`, {
|
|
2229
2315
|
method: "POST",
|
|
@@ -2241,14 +2327,14 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2241
2327
|
await this.throwOpenRouterFetchError(response2);
|
|
2242
2328
|
}
|
|
2243
2329
|
const data = await response2.json();
|
|
2244
|
-
return transformResponse(data);
|
|
2330
|
+
return this.attachUsagePricing(transformResponse(data), pricingPromise);
|
|
2245
2331
|
}
|
|
2246
2332
|
const client = await this.getClient();
|
|
2247
2333
|
const response = await client.beta.responses.send({
|
|
2248
2334
|
...this.toSdkResponsesRequest(params),
|
|
2249
2335
|
stream: false
|
|
2250
2336
|
});
|
|
2251
|
-
return transformResponse(response);
|
|
2337
|
+
return this.attachUsagePricing(transformResponse(response), pricingPromise);
|
|
2252
2338
|
} catch (error) {
|
|
2253
2339
|
throw this.toProviderError(error);
|
|
2254
2340
|
}
|
|
@@ -2272,6 +2358,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2272
2358
|
if (this.config.providers && this.config.providers.length > 0) {
|
|
2273
2359
|
params.provider = { only: this.config.providers };
|
|
2274
2360
|
}
|
|
2361
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2275
2362
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
2276
2363
|
method: "POST",
|
|
2277
2364
|
headers: {
|
|
@@ -2311,6 +2398,10 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2311
2398
|
}
|
|
2312
2399
|
}
|
|
2313
2400
|
if (finishChunk) {
|
|
2401
|
+
if (finishChunk.type === "finish") {
|
|
2402
|
+
const pricing = await _OpenRouterProvider.settleUsagePricing(pricingPromise);
|
|
2403
|
+
if (pricing) finishChunk.usage.pricing = pricing;
|
|
2404
|
+
}
|
|
2314
2405
|
const finishWithMeta = {
|
|
2315
2406
|
...finishChunk,
|
|
2316
2407
|
_asyncMetadata: responseId ? {
|
|
@@ -2341,6 +2432,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2341
2432
|
try {
|
|
2342
2433
|
const params = buildCreateParams(request);
|
|
2343
2434
|
this.applyConfiguredProvidersToResponses(params);
|
|
2435
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2344
2436
|
const response = await fetch(`${baseUrl}/responses`, {
|
|
2345
2437
|
method: "POST",
|
|
2346
2438
|
headers: {
|
|
@@ -2379,6 +2471,10 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2379
2471
|
}
|
|
2380
2472
|
}
|
|
2381
2473
|
if (finishChunk) {
|
|
2474
|
+
if (finishChunk.type === "finish") {
|
|
2475
|
+
const pricing = await _OpenRouterProvider.settleUsagePricing(pricingPromise);
|
|
2476
|
+
if (pricing) finishChunk.usage.pricing = pricing;
|
|
2477
|
+
}
|
|
2382
2478
|
const finishWithMeta = {
|
|
2383
2479
|
...finishChunk,
|
|
2384
2480
|
// Include what's needed to fetch metadata later
|