@standardagents/openrouter 0.24.4 → 0.24.6
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 +100 -10
- 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
|
@@ -1944,6 +1944,25 @@ function getModelIconDataUri(modelId) {
|
|
|
1944
1944
|
}
|
|
1945
1945
|
|
|
1946
1946
|
// src/OpenRouterProvider.ts
|
|
1947
|
+
function usagePricingFromCatalogPricing(pricing) {
|
|
1948
|
+
if (!pricing) return void 0;
|
|
1949
|
+
const perMillion = (value) => {
|
|
1950
|
+
if (value === void 0) return void 0;
|
|
1951
|
+
const parsed = Number.parseFloat(value);
|
|
1952
|
+
if (!Number.isFinite(parsed)) return void 0;
|
|
1953
|
+
return Number((parsed * 1e6).toFixed(12));
|
|
1954
|
+
};
|
|
1955
|
+
const inputPerMillion = perMillion(pricing.prompt);
|
|
1956
|
+
const outputPerMillion = perMillion(pricing.completion);
|
|
1957
|
+
if (inputPerMillion === void 0 || outputPerMillion === void 0) {
|
|
1958
|
+
return void 0;
|
|
1959
|
+
}
|
|
1960
|
+
return {
|
|
1961
|
+
inputPerMillion,
|
|
1962
|
+
cachedInputPerMillion: perMillion(pricing.input_cache_read),
|
|
1963
|
+
outputPerMillion
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1947
1966
|
var OpenRouterProvider = class _OpenRouterProvider {
|
|
1948
1967
|
name = "openrouter";
|
|
1949
1968
|
specificationVersion = "1";
|
|
@@ -2075,6 +2094,11 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2075
2094
|
static modelsCacheTime = 0;
|
|
2076
2095
|
static CACHE_TTL = 5 * 60 * 1e3;
|
|
2077
2096
|
// 5 minutes
|
|
2097
|
+
/** In-flight catalog download shared by concurrent callers. */
|
|
2098
|
+
static modelsFetchPromise = null;
|
|
2099
|
+
/** Timestamp of the last failed catalog fetch, for negative caching. */
|
|
2100
|
+
static modelsFetchFailedAt = 0;
|
|
2101
|
+
static FAILURE_RETRY_MS = 60 * 1e3;
|
|
2078
2102
|
/**
|
|
2079
2103
|
* Get capabilities for a specific model.
|
|
2080
2104
|
* Fetches from OpenRouter's models API which provides rich metadata.
|
|
@@ -2092,19 +2116,73 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2092
2116
|
return null;
|
|
2093
2117
|
}
|
|
2094
2118
|
}
|
|
2119
|
+
/**
|
|
2120
|
+
* How long a streaming finish chunk may wait on the catalog pricing lookup.
|
|
2121
|
+
* The lookup is started when the stream starts, so by finish time it is
|
|
2122
|
+
* almost always resolved; the bound guarantees a slow catalog download can
|
|
2123
|
+
* never stall the finish chunk into the runtime's stream watchdog.
|
|
2124
|
+
*/
|
|
2125
|
+
static USAGE_PRICING_FINISH_WAIT_MS = 2500;
|
|
2126
|
+
static async settleUsagePricing(pricingPromise) {
|
|
2127
|
+
let timer;
|
|
2128
|
+
const timeout = new Promise((resolve) => {
|
|
2129
|
+
timer = setTimeout(() => resolve(void 0), _OpenRouterProvider.USAGE_PRICING_FINISH_WAIT_MS);
|
|
2130
|
+
});
|
|
2131
|
+
try {
|
|
2132
|
+
return await Promise.race([pricingPromise, timeout]);
|
|
2133
|
+
} finally {
|
|
2134
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
/** Bounded-wait pricing attachment shared by the non-streaming paths. */
|
|
2138
|
+
async attachUsagePricing(result, pricingPromise) {
|
|
2139
|
+
const pricing = await _OpenRouterProvider.settleUsagePricing(pricingPromise);
|
|
2140
|
+
if (pricing) result.usage.pricing = pricing;
|
|
2141
|
+
return result;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Exact unit prices for a model from OpenRouter's live catalog (cached),
|
|
2145
|
+
* attached to response usage so request logs can record specific prices.
|
|
2146
|
+
* Best-effort: any failure just leaves pricing off the usage payload.
|
|
2147
|
+
*/
|
|
2148
|
+
async getUsagePricing(modelId) {
|
|
2149
|
+
if (!modelId) return void 0;
|
|
2150
|
+
try {
|
|
2151
|
+
const models = await this.fetchModelsWithCache();
|
|
2152
|
+
const model = models.find((m) => m.id === modelId);
|
|
2153
|
+
return usagePricingFromCatalogPricing(model?.pricing);
|
|
2154
|
+
} catch {
|
|
2155
|
+
return void 0;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2095
2158
|
async fetchModelsWithCache() {
|
|
2096
2159
|
const now = Date.now();
|
|
2097
2160
|
if (_OpenRouterProvider.modelsCache && now - _OpenRouterProvider.modelsCacheTime < _OpenRouterProvider.CACHE_TTL) {
|
|
2098
2161
|
return _OpenRouterProvider.modelsCache;
|
|
2099
2162
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
throw new Error(`OpenRouter API error: ${response.status}`);
|
|
2163
|
+
if (now - _OpenRouterProvider.modelsFetchFailedAt < _OpenRouterProvider.FAILURE_RETRY_MS) {
|
|
2164
|
+
throw new Error("OpenRouter models fetch recently failed");
|
|
2103
2165
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2166
|
+
if (!_OpenRouterProvider.modelsFetchPromise) {
|
|
2167
|
+
_OpenRouterProvider.modelsFetchPromise = (async () => {
|
|
2168
|
+
try {
|
|
2169
|
+
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
2170
|
+
if (!response.ok) {
|
|
2171
|
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
|
2172
|
+
}
|
|
2173
|
+
const data = await response.json();
|
|
2174
|
+
_OpenRouterProvider.modelsCache = data.data || [];
|
|
2175
|
+
_OpenRouterProvider.modelsCacheTime = Date.now();
|
|
2176
|
+
return _OpenRouterProvider.modelsCache;
|
|
2177
|
+
} catch (error) {
|
|
2178
|
+
_OpenRouterProvider.modelsFetchFailedAt = Date.now();
|
|
2179
|
+
throw error;
|
|
2180
|
+
} finally {
|
|
2181
|
+
_OpenRouterProvider.modelsFetchPromise = null;
|
|
2182
|
+
}
|
|
2183
|
+
})();
|
|
2184
|
+
}
|
|
2185
|
+
return _OpenRouterProvider.modelsFetchPromise;
|
|
2108
2186
|
}
|
|
2109
2187
|
mapOpenRouterCapabilities(model) {
|
|
2110
2188
|
const capabilities = {
|
|
@@ -2189,6 +2267,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2189
2267
|
if (this.config.providers && this.config.providers.length > 0) {
|
|
2190
2268
|
params.provider = { only: this.config.providers };
|
|
2191
2269
|
}
|
|
2270
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2192
2271
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
2193
2272
|
method: "POST",
|
|
2194
2273
|
headers: {
|
|
@@ -2210,7 +2289,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2210
2289
|
throw new ProviderError(errorMessage, "invalid_request", response.status);
|
|
2211
2290
|
}
|
|
2212
2291
|
const data = await response.json();
|
|
2213
|
-
return transformChatResponse(data);
|
|
2292
|
+
return this.attachUsagePricing(transformChatResponse(data), pricingPromise);
|
|
2214
2293
|
} catch (error) {
|
|
2215
2294
|
throw this.toProviderError(error);
|
|
2216
2295
|
}
|
|
@@ -2224,6 +2303,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2224
2303
|
try {
|
|
2225
2304
|
const params = buildCreateParams(request);
|
|
2226
2305
|
this.applyConfiguredProvidersToResponses(params);
|
|
2306
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2227
2307
|
if (this.hasOpenRouterServerTools(params)) {
|
|
2228
2308
|
const response2 = await fetch(`${baseUrl}/responses`, {
|
|
2229
2309
|
method: "POST",
|
|
@@ -2241,14 +2321,14 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2241
2321
|
await this.throwOpenRouterFetchError(response2);
|
|
2242
2322
|
}
|
|
2243
2323
|
const data = await response2.json();
|
|
2244
|
-
return transformResponse(data);
|
|
2324
|
+
return this.attachUsagePricing(transformResponse(data), pricingPromise);
|
|
2245
2325
|
}
|
|
2246
2326
|
const client = await this.getClient();
|
|
2247
2327
|
const response = await client.beta.responses.send({
|
|
2248
2328
|
...this.toSdkResponsesRequest(params),
|
|
2249
2329
|
stream: false
|
|
2250
2330
|
});
|
|
2251
|
-
return transformResponse(response);
|
|
2331
|
+
return this.attachUsagePricing(transformResponse(response), pricingPromise);
|
|
2252
2332
|
} catch (error) {
|
|
2253
2333
|
throw this.toProviderError(error);
|
|
2254
2334
|
}
|
|
@@ -2272,6 +2352,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2272
2352
|
if (this.config.providers && this.config.providers.length > 0) {
|
|
2273
2353
|
params.provider = { only: this.config.providers };
|
|
2274
2354
|
}
|
|
2355
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2275
2356
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
2276
2357
|
method: "POST",
|
|
2277
2358
|
headers: {
|
|
@@ -2311,6 +2392,10 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2311
2392
|
}
|
|
2312
2393
|
}
|
|
2313
2394
|
if (finishChunk) {
|
|
2395
|
+
if (finishChunk.type === "finish") {
|
|
2396
|
+
const pricing = await _OpenRouterProvider.settleUsagePricing(pricingPromise);
|
|
2397
|
+
if (pricing) finishChunk.usage.pricing = pricing;
|
|
2398
|
+
}
|
|
2314
2399
|
const finishWithMeta = {
|
|
2315
2400
|
...finishChunk,
|
|
2316
2401
|
_asyncMetadata: responseId ? {
|
|
@@ -2341,6 +2426,7 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2341
2426
|
try {
|
|
2342
2427
|
const params = buildCreateParams(request);
|
|
2343
2428
|
this.applyConfiguredProvidersToResponses(params);
|
|
2429
|
+
const pricingPromise = this.getUsagePricing(params.model);
|
|
2344
2430
|
const response = await fetch(`${baseUrl}/responses`, {
|
|
2345
2431
|
method: "POST",
|
|
2346
2432
|
headers: {
|
|
@@ -2379,6 +2465,10 @@ var OpenRouterProvider = class _OpenRouterProvider {
|
|
|
2379
2465
|
}
|
|
2380
2466
|
}
|
|
2381
2467
|
if (finishChunk) {
|
|
2468
|
+
if (finishChunk.type === "finish") {
|
|
2469
|
+
const pricing = await _OpenRouterProvider.settleUsagePricing(pricingPromise);
|
|
2470
|
+
if (pricing) finishChunk.usage.pricing = pricing;
|
|
2471
|
+
}
|
|
2382
2472
|
const finishWithMeta = {
|
|
2383
2473
|
...finishChunk,
|
|
2384
2474
|
// Include what's needed to fetch metadata later
|