@plurnk/plurnk-providers 1.18.0 → 1.19.0

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 (67) hide show
  1. package/.env.defaults +8 -4
  2. package/README.md +4 -16
  3. package/SPEC.md +41 -11
  4. package/dist/AiSdkProvider.d.ts +2 -4
  5. package/dist/AiSdkProvider.d.ts.map +1 -1
  6. package/dist/AiSdkProvider.js +7 -17
  7. package/dist/AiSdkProvider.js.map +1 -1
  8. package/dist/AiSdkRequestBody.d.ts +1 -4
  9. package/dist/AiSdkRequestBody.d.ts.map +1 -1
  10. package/dist/AiSdkRequestBody.js +10 -48
  11. package/dist/AiSdkRequestBody.js.map +1 -1
  12. package/dist/Mock.d.ts +2 -2
  13. package/dist/Mock.d.ts.map +1 -1
  14. package/dist/Mock.js +3 -3
  15. package/dist/Mock.js.map +1 -1
  16. package/dist/ProviderRegistry.js +2 -2
  17. package/dist/ProviderRegistry.js.map +1 -1
  18. package/dist/accounting.d.ts +0 -1
  19. package/dist/accounting.d.ts.map +1 -1
  20. package/dist/accounting.js +1 -7
  21. package/dist/accounting.js.map +1 -1
  22. package/dist/aiSdkTransport.d.ts.map +1 -1
  23. package/dist/aiSdkTransport.js +79 -7
  24. package/dist/aiSdkTransport.js.map +1 -1
  25. package/dist/catalogProvider.d.ts.map +1 -1
  26. package/dist/catalogProvider.js +13 -3
  27. package/dist/catalogProvider.js.map +1 -1
  28. package/dist/compatibleProvider.d.ts +1 -1
  29. package/dist/compatibleProvider.d.ts.map +1 -1
  30. package/dist/compatibleProvider.js +13 -25
  31. package/dist/compatibleProvider.js.map +1 -1
  32. package/dist/env.d.ts.map +1 -1
  33. package/dist/env.js +1 -0
  34. package/dist/env.js.map +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/types.d.ts +0 -6
  40. package/dist/types.d.ts.map +1 -1
  41. package/package.json +7 -7
  42. package/src/AiSdkProvider.test.ts +97 -126
  43. package/src/AiSdkProvider.ts +8 -22
  44. package/src/AiSdkRequestBody.ts +10 -42
  45. package/src/Mock.test.ts +11 -3
  46. package/src/Mock.ts +3 -3
  47. package/src/ProviderRegistry.test.ts +0 -1
  48. package/src/ProviderRegistry.ts +1 -1
  49. package/src/accounting.test.ts +0 -11
  50. package/src/accounting.ts +0 -8
  51. package/src/aiSdkTransport.test.ts +63 -1
  52. package/src/aiSdkTransport.ts +82 -7
  53. package/src/boundaries.test.ts +1 -1
  54. package/src/capacity.test.ts +2 -2
  55. package/src/catalogProvider.test.ts +46 -1
  56. package/src/catalogProvider.ts +12 -3
  57. package/src/compatibleProvider.test.ts +6 -6
  58. package/src/compatibleProvider.ts +13 -28
  59. package/src/cost.test.ts +3 -3
  60. package/src/discover.test.ts +11 -2
  61. package/src/env.test.ts +3 -0
  62. package/src/env.ts +2 -1
  63. package/src/errors.test.ts +2 -2
  64. package/src/index.ts +0 -1
  65. package/src/inputModalities.test.ts +2 -1
  66. package/src/sdkModels.test.ts +1 -1
  67. package/src/types.ts +8 -35
@@ -15,7 +15,7 @@ const apiError = (statusCode: number, responseBody = "body") => new APICallError
15
15
 
16
16
  const SOURCE_PATTERN = /^[a-z]+(:[a-z][a-z0-9-]*)?$/;
17
17
 
18
- test("HTTP request rejection preserves its cause without becoming a response-contract strike", () => {
18
+ test("{§provider-request-rejection} HTTP request rejection preserves its cause without becoming a response-contract strike", () => {
19
19
  for (const status of [400, 404, 405, 422]) {
20
20
  const message = "The requested model is not available on this endpoint.";
21
21
  const cause = apiError(status, JSON.stringify({ error: { message } }));
@@ -54,7 +54,7 @@ test("classifyProviderError maps HTTP status to kind", () => {
54
54
  assert.equal(k(404), "request_rejected");
55
55
  });
56
56
 
57
- test("capacity normalization prefers structured provider codes and keeps generic 400s distinct", () => {
57
+ test("{§provider-capacity-failure} capacity normalization prefers structured provider codes and keeps generic 400s distinct", () => {
58
58
  const openai = apiError(400, JSON.stringify({
59
59
  error: {
60
60
  type: "invalid_request_error",
package/src/index.ts CHANGED
@@ -78,7 +78,6 @@ export {
78
78
  } from "./cost.ts";
79
79
  export {
80
80
  aggregateProviderAccounting,
81
- plurnkCostNormalizer,
82
81
  providerCostNormalizer,
83
82
  validateProviderRequestAccounting,
84
83
  } from "./accounting.ts";
@@ -16,6 +16,7 @@ const parts: ChatMessage = {
16
16
  { type: "text", text: "look at this" },
17
17
  { type: "file", data: PNG, mediaType: "image/png" },
18
18
  { type: "file", data: PDF, mediaType: "application/pdf" },
19
+ { type: "file", data: new Uint8Array([82, 73, 70, 70]), mediaType: "audio/wav" },
19
20
  ],
20
21
  };
21
22
 
@@ -37,7 +38,7 @@ test("{§provider-input-modalities} the Mock declares modalities by option and r
37
38
  const seeing = new Mock({ contextWindow: 200000, responses: [], inputModalities: ["image", "pdf"] });
38
39
  assert.ok(seeing.inputModalities.has("image") && seeing.inputModalities.has("pdf") && !seeing.inputModalities.has("audio"));
39
40
  assert.equal(tokensOf(await seeing.countPromptTokens([parts])), Math.ceil("look at this".length / 2));
40
- await assert.rejects(seeing.generate({ messages: [parts], maxOutputTokens: 16, workerId: "1", primaryWorkerId: "1", callKind: "bare" } as never));
41
+ await assert.rejects(seeing.generate({ messages: [parts], maxOutputTokens: 16, workerId: "1", callKind: "bare" } as never));
41
42
  assert.equal(seeing.received.length, 1, "the request was recorded before the empty queue refused it");
42
43
  const received = seeing.received[0]?.[0]?.content;
43
44
  assert.ok(Array.isArray(received) && received[1]?.type === "file" && received[1].mediaType === "image/png" && received[2]?.type === "file" && received[2].mediaType === "application/pdf");
@@ -103,7 +103,7 @@ test("{§model-catalog-readiness}: an authenticated operator declaration reports
103
103
  );
104
104
  });
105
105
 
106
- test("createSdkModel uses Models.dev provider facts and operator credentials", () => {
106
+ test("{§provider-sdk-boundary} createSdkModel uses Models.dev provider facts and operator credentials", () => {
107
107
  const sdk = createSdkModel("xai", "grok-build-0.1", { XAI_API_KEY: "test-key" });
108
108
  assert.notEqual(sdk, null);
109
109
  assert.equal(sdk?.catalog?.npm, "@ai-sdk/xai");
package/src/types.ts CHANGED
@@ -230,16 +230,13 @@ export type ProviderAttempt = ProviderResponse<ProviderAttemptFinishReason>;
230
230
  export interface ProviderGenerateArgs {
231
231
  readonly messages: ChatMessage[];
232
232
  readonly workerId: string;
233
- readonly primaryWorkerId?: string;
234
233
  readonly signal?: AbortSignal;
235
234
  readonly grammar?: string;
236
235
  readonly maxOutputTokens?: number;
237
- readonly attributions?: string[];
238
- readonly client?: string;
239
- readonly strikes?: number;
236
+ // The workspace this call serves: ordinary call context for an adapter that
237
+ // fronts more than one workspace. It is never transported to a backend and
238
+ // never model-facing.
240
239
  readonly workspaceId?: string;
241
- readonly loop?: number;
242
- readonly turn?: number;
243
240
  readonly sampling?: Record<string, unknown>;
244
241
  readonly observeRequest?: ProviderRequestObserver;
245
242
  readonly observeReasoning?: ProviderReasoningObserver;
@@ -265,42 +262,18 @@ export interface Provider {
265
262
  // its content. The consumer never sees or chooses backend resources
266
263
  // (slot integers, connections); the mechanism is the provider's.
267
264
  //
268
- // `attributions` is opaque consumer-supplied creator telemetry; the consumer
269
- // owns what contribution that set claims ({§attribution}). `client` is the
270
- // consumer's workspace-stable, self-identified frontend. They are forwarded ONLY by a
271
- // provider whose spec opts in (the first-party `plurnk` endpoint, via
272
- // `Plurnk-Attribution` / `Plurnk-Client` headers); every other provider DROPS
273
- // them — the gate is structural so first-party metadata can never leak to a
274
- // third-party backend.
275
- //
276
265
  // `sampling` is an optional bag of standard OpenAI-compat sampling params
277
266
  // (temperature, top_p, top_k, min_p, penalties, stop, seed, …) forwarded into
278
267
  // the request body UNDER the provider's managed fields — model/messages/grammar/
279
268
  // reasoning/max_tokens/slot always win, and transport/protocol keys (stream,
280
269
  // response_format, grammar, id_slot) are stripped, so it carries sampling intent
281
270
  // only and can't bypass grammar transport ({§provider-request-authority}). A
282
- // proxy consumer (the
283
- // plurnk endpoint fronting its own backends) uses it to pass its caller's sampling
284
- // knobs through; a direct consumer typically leaves it unset.
285
- //
286
- // `strikes` is the worker's CURRENT rail-strike streak at time-of-generate
287
- // (0 = clean; a clean turn zeroes it; every loop starts at 0 — contract
288
- // {§strikes-first-party-metadata}). Forwarded as a `Plurnk-Strikes` header ONLY under the
289
- // same firstPartyMetadata gate as attributions/client; dropped everywhere
290
- // else. Headers only — the packet NEVER carries strike state (the model must
291
- // not see engine accounting; it would become a metric to game).
292
- //
293
- // `workspaceId`/`loop`/`turn` are the turn coordinate ({§lifecycle-terms}) — the
294
- // daemon-side sequence of the turn being generated, which the endpoint can
295
- // never scrape from the wire. Forwarded as `Plurnk-Workspace-Id`/`Plurnk-Loop`/
296
- // `Plurnk-Turn` ONLY under the same firstPartyMetadata gate; dropped
297
- // everywhere else. Coordinates are 1-based: absent/0 emits no header (no
298
- // strikes-style zero exception). Headers only, never the packet.
271
+ // proxy consumer fronting its own backends uses it to pass its caller's
272
+ // sampling knobs through; a direct consumer typically leaves it unset.
299
273
  //
300
- // `callKind` is the caller's explicit output contract. It is transported as
301
- // `Plurnk-Call-Kind` only under the first-party metadata gate and never
302
- // inferred from the request shape. Generic callers may omit it; Core always
303
- // supplies `emission` or `bare`.
274
+ // `callKind` is the caller's explicit output contract, never inferred from
275
+ // the request shape. Generic callers may omit it; Core always supplies
276
+ // `emission` or `bare`.
304
277
  generate(args: ProviderGenerateArgs): Promise<ProviderResponse>;
305
278
  // {§model-fact-resolution} — effective total context envelope in tokens,
306
279
  // including any stricter operator cap. `null` means unknown; under