@absolutejs/ai 0.0.52 → 0.0.53

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/README.md CHANGED
@@ -170,7 +170,10 @@ message transforms, native reasoning controls, prompt and response caching,
170
170
  text-plus-audio output, verbosity, user attribution, and an `extraBody` escape
171
171
  hatch for new OpenRouter parameters. The escape hatch cannot replace models,
172
172
  fallbacks, providers, presets, messages, plugins, or tools; those fields use
173
- policy-aware typed options instead.
173
+ policy-aware typed options instead. Advisor, Fusion, Shell, Subagent, model
174
+ search, web search/fetch, image generation, Datetime, and Apply Patch server
175
+ tools have typed wire parameters and documented range checks. OpenRouter's old
176
+ `web` plugin is deprecated; use `openrouter:web_search` instead.
174
177
 
175
178
  URL images and PDFs, base64 audio, and URL/base64 video inputs use the ordinary
176
179
  AbsoluteJS content-block contract. URL citations are emitted as `citation`
@@ -213,6 +216,14 @@ const reranked = await openrouterClient.rerank({
213
216
  documents: ["response caching", "CSS layout"],
214
217
  });
215
218
 
219
+ // Response healing is documented for non-streaming structured responses.
220
+ const healed = await openrouterClient.chat({
221
+ model: "anthropic/claude-sonnet-4.6",
222
+ messages: [{ role: "user", content: "Return a JSON object" }],
223
+ response_format: { type: "json_object" },
224
+ plugins: [{ id: "response-healing" }],
225
+ });
226
+
216
227
  const batch = await openrouterClient.createBatch({
217
228
  endpoint: "/v1/chat/completions",
218
229
  model: "anthropic/claude-sonnet-4.6",
@@ -249,6 +260,36 @@ const { key } = await exchangeOpenRouterAuthCode({
249
260
  });
250
261
  ```
251
262
 
263
+ ### Complete OpenRouter management SDK
264
+
265
+ The inference adapter stays small and policy-aware. The separate
266
+ `@absolutejs/ai/openrouter/sdk` entry point configures and exposes OpenRouter's
267
+ official OpenAPI-generated TypeScript SDK for the complete administrative and
268
+ data surface:
269
+
270
+ ```ts
271
+ import { createOpenRouterSDK } from "@absolutejs/ai/openrouter/sdk";
272
+
273
+ const openrouterAdmin = createOpenRouterSDK({
274
+ tokenSource: getRotatingManagementKey,
275
+ appName: "My AbsoluteJS App",
276
+ appUrl: "https://example.com",
277
+ timeoutMs: 15_000,
278
+ retryConfig: { strategy: "backoff" },
279
+ });
280
+
281
+ const keys = await openrouterAdmin.apiKeys.list();
282
+ const guardrails = await openrouterAdmin.guardrails.list();
283
+ const embeddingModels = await openrouterAdmin.embeddings.listModels();
284
+ ```
285
+
286
+ This entry point includes API-key management, BYOK, guardrails and assignments,
287
+ observability destinations, organizations, SCIM, workspaces, public datasets,
288
+ benchmarks, typed pagination, retries, per-call timeouts, and abort signals. It
289
+ tracks compatible patch releases of OpenRouter's generated SDK so new OpenAPI
290
+ fields do not depend on a handwritten AbsoluteJS type update. Normal
291
+ `@absolutejs/ai/openrouter` imports do not load this management surface.
292
+
252
293
  For a strict model-origin policy, also assign an OpenRouter key/workspace
253
294
  guardrail with the same model allowlist. Provider allowlists restrict where a
254
295
  model runs; they do not identify who developed it. Presets and router aliases
@@ -257,8 +298,7 @@ the request. The raw client is intentionally unopinionated and should be limited
257
298
  to trusted server-side administration code. OpenRouter currently documents
258
299
  reporting generation feedback through Chatroom and Logs, not through a public
259
300
  feedback API, so AbsoluteJS exposes generation IDs/content without inventing an
260
- unstable endpoint. OpenRouter's official SDK can be used alongside this package
261
- for specialized organization, SSO, SCIM, and BYOK administration.
301
+ unstable endpoint.
262
302
 
263
303
  Use `openrouterResponses(config)` when an AbsoluteJS agent should stream through
264
304
  OpenRouter's stateless Responses API, or `openrouterMessages(config)` for the
package/dist/ai/index.js CHANGED
@@ -2850,6 +2850,13 @@ var createOpenRouterClient = (config2) => {
2850
2850
  },
2851
2851
  method: "POST"
2852
2852
  }),
2853
+ chat: (body) => {
2854
+ assertAllowedModelsInValue(body, allowedModels);
2855
+ return request("/chat/completions", {
2856
+ body: { ...body, stream: false },
2857
+ method: "POST"
2858
+ });
2859
+ },
2853
2860
  createPresetFromChatCompletions: (slug, body) => {
2854
2861
  assertAllowedModelsInValue(body, allowedModels);
2855
2862
  return request(`/presets/${encodeURIComponent(slug)}/chat/completions`, { body, method: "POST" });
@@ -2952,6 +2959,7 @@ var createOpenRouterClient = (config2) => {
2952
2959
  getVideo: (id) => request(`/videos/${encodeURIComponent(id)}`),
2953
2960
  getWorkspace: (id) => request(`/workspaces/${encodeURIComponent(id)}`),
2954
2961
  listImageModels: async () => filterModelList(await request("/images/models")),
2962
+ listEmbeddingModels: async () => filterModelList(await request("/embeddings/models")),
2955
2963
  listFiles: (query) => request("/files", {
2956
2964
  query: {
2957
2965
  ...query,
@@ -3223,6 +3231,84 @@ var assertIndirectModels = (value, allowedModels, key = "") => {
3223
3231
  assertIndirectModels(child, allowedModels, childKey);
3224
3232
  }
3225
3233
  };
3234
+ var assertIntegerRange = (label, value, minimum, maximum) => {
3235
+ if (value === undefined)
3236
+ return;
3237
+ if (!Number.isInteger(value) || value < minimum || maximum !== undefined && value > maximum)
3238
+ throw new Error(`OpenRouter ${label} must be an integer from ${minimum}${maximum === undefined ? " or greater" : ` to ${maximum}`}`);
3239
+ };
3240
+ var assertPluginOptions = (plugins) => {
3241
+ for (const plugin of plugins ?? []) {
3242
+ if (plugin.id === "response-healing")
3243
+ throw new Error("OpenRouter response-healing requires a non-streaming request; use createOpenRouterClient().chat()");
3244
+ if (plugin.id === "fusion") {
3245
+ const fusion = plugin;
3246
+ if (fusion.analysis_models && (fusion.analysis_models.length < 1 || fusion.analysis_models.length > 8))
3247
+ throw new Error("OpenRouter Fusion analysis_models must contain 1-8 models");
3248
+ assertIntegerRange("Fusion max_tool_calls", fusion.max_tool_calls, 1, 16);
3249
+ }
3250
+ if (plugin.id === "web") {
3251
+ const web = plugin;
3252
+ assertIntegerRange("web plugin max_results", web.max_results, 1, web.engine === "perplexity" ? 20 : 25);
3253
+ if (web.include_domains?.length && web.exclude_domains?.length && (web.engine === "firecrawl" || web.engine === "parallel" || web.engine === "perplexity"))
3254
+ throw new Error(`OpenRouter ${web.engine} web plugin cannot combine include_domains and exclude_domains`);
3255
+ }
3256
+ }
3257
+ };
3258
+ var assertServerToolOptions = (tools) => {
3259
+ for (const tool of tools ?? []) {
3260
+ if (tool.type === "openrouter:web_search") {
3261
+ const parameters = tool.parameters;
3262
+ assertIntegerRange("web search max_results", parameters?.max_results, 1, 25);
3263
+ if (parameters?.engine === "perplexity" && (parameters.max_results ?? 0) > 20)
3264
+ throw new Error("OpenRouter Perplexity web search max_results must be at most 20");
3265
+ assertIntegerRange("web search max_characters", parameters?.max_characters, 1, 1e5);
3266
+ assertIntegerRange("web search max_total_results", parameters?.max_total_results, 1);
3267
+ }
3268
+ if (tool.type === "openrouter:web_fetch") {
3269
+ assertIntegerRange("web fetch max_uses", tool.parameters?.max_uses, 1);
3270
+ assertIntegerRange("web fetch max_content_tokens", tool.parameters?.max_content_tokens, 1);
3271
+ }
3272
+ if (tool.type === "openrouter:image_generation") {
3273
+ const compression = tool.parameters?.output_compression;
3274
+ if (compression !== undefined && (!Number.isFinite(compression) || compression < 0 || compression > 100))
3275
+ throw new Error("OpenRouter image generation output_compression must be 0-100");
3276
+ }
3277
+ if (tool.type === "openrouter:subagent") {
3278
+ assertIntegerRange("subagent max_completion_tokens", tool.parameters.max_completion_tokens, 1);
3279
+ assertIntegerRange("subagent max_tool_calls", tool.parameters.max_tool_calls, 1, 25);
3280
+ const temperature = tool.parameters.temperature;
3281
+ if (temperature !== undefined && (!Number.isFinite(temperature) || temperature < 0 || temperature > 2))
3282
+ throw new Error("OpenRouter subagent temperature must be 0-2");
3283
+ assertServerToolOptions(tool.parameters.tools);
3284
+ }
3285
+ if (tool.type === "openrouter:advisor") {
3286
+ assertIntegerRange("advisor max_completion_tokens", tool.parameters?.max_completion_tokens, 1);
3287
+ const temperature = tool.parameters?.temperature;
3288
+ if (temperature !== undefined && (!Number.isFinite(temperature) || temperature < 0 || temperature > 2))
3289
+ throw new Error("OpenRouter advisor temperature must be 0-2");
3290
+ }
3291
+ if (tool.type === "openrouter:fusion") {
3292
+ const analysisModels = tool.parameters?.analysis_models;
3293
+ if (analysisModels && (analysisModels.length < 1 || analysisModels.length > 8))
3294
+ throw new Error("OpenRouter Fusion server tool analysis_models must contain 1-8 models");
3295
+ assertIntegerRange("Fusion server tool max_completion_tokens", tool.parameters?.max_completion_tokens, 1);
3296
+ assertIntegerRange("Fusion server tool max_tool_calls", tool.parameters?.max_tool_calls, 1, 16);
3297
+ const temperature = tool.parameters?.temperature;
3298
+ if (temperature !== undefined && (!Number.isFinite(temperature) || temperature < 0 || temperature > 2))
3299
+ throw new Error("OpenRouter Fusion server tool temperature must be 0-2");
3300
+ assertServerToolOptions(tool.parameters?.tools);
3301
+ }
3302
+ if (tool.type === "openrouter:shell") {
3303
+ assertIntegerRange("shell sleep_after_seconds", tool.parameters?.sleep_after_seconds, 0, 2592000);
3304
+ const environment = tool.parameters?.environment;
3305
+ if (environment?.type === "container_reference" && (environment.container_id.length < 1 || environment.container_id.length > 20))
3306
+ throw new Error("OpenRouter shell container_id must contain 1-20 characters");
3307
+ }
3308
+ if (tool.type === "openrouter:experimental__search_models")
3309
+ assertIntegerRange("model search max_results", tool.parameters?.max_results, 1, 20);
3310
+ }
3311
+ };
3226
3312
  var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProviders) => {
3227
3313
  assertAllowedPreset(options.preset, allowedPresets);
3228
3314
  assertRequestRoutingPolicy(options.routing, allowedProviders);
@@ -3242,6 +3328,8 @@ var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProvi
3242
3328
  assertIndirectModels(options.serverTools, allowedModels);
3243
3329
  assertIndirectModels(options.messagesTools, allowedModels);
3244
3330
  assertIndirectModels(options.plugins, allowedModels);
3331
+ assertPluginOptions(options.plugins);
3332
+ assertServerToolOptions(options.serverTools);
3245
3333
  if (options.extraBody) {
3246
3334
  const unsafe = Object.keys(options.extraBody).find((key) => SECURITY_SENSITIVE_EXTRA_BODY_FIELDS.has(key));
3247
3335
  if (unsafe)
@@ -7580,5 +7668,5 @@ export {
7580
7668
  BUILTIN_UI_CARDS
7581
7669
  };
7582
7670
 
7583
- //# debugId=25187F8D2370EC5064756E2164756E21
7671
+ //# debugId=1A3329E4751DCFB764756E2164756E21
7584
7672
  //# sourceMappingURL=index.js.map