flexinference 1.2.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0
4
+
5
+ Surfaces the router's **`doc_url`** on `FlexInferenceError` as a new `docUrl` field: a deep
6
+ link straight to the docs row for that error's `code` (when the router supplied one), so an
7
+ error caught in code can point a developer -- or an agent reading the message -- at the fix.
8
+ Also enriches the local `start_within` validation messages with concrete examples. Additive;
9
+ no breaking changes.
10
+
11
+ ## 1.3.0
12
+
13
+ Adds a typed **`PaymentRequiredError`** (a subclass of `FlexInferenceError`) thrown on
14
+ HTTP `402`, which the router returns when billing is past due and billable flex is paused
15
+ (free routing still works). Catch it on its own to prompt the user to update payment;
16
+ existing `catch (err instanceof FlexInferenceError)` handlers keep catching 402s because
17
+ it is a subclass. No other API or type changes.
18
+
3
19
  ## 1.2.0
4
20
 
5
21
  Adds the **Messages** caller format (`client.messages.create`), the fourth format
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # FlexInference (TypeScript)
2
2
 
3
- The official TypeScript SDK for [FlexInference](https://flexinference.com) - a deadline-aware, OpenAI-compatible inference router. Send the OpenAI requests you already send, bring your own OpenAI key, and set one required field - `start_within` - to trade latency for cost.
3
+ The official TypeScript SDK for [FlexInference](https://flexinference.com) - a deadline-aware inference router across **OpenAI, Google Gemini, and Anthropic**. Send the OpenAI requests you already send, bring your own provider key, and set one required field - `start_within` - to trade latency for cost. Four caller formats are supported: `responses`, `chat.completions`, `interactions` (Gemini shape), and `messages` (Anthropic shape) - any of them reaches any provider.
4
4
 
5
5
  ```bash
6
6
  npm install flexinference
@@ -26,14 +26,15 @@ Responses come back as the **raw OpenAI JSON** (we never reshape the body), so t
26
26
 
27
27
  `start_within` is **required** on every request. It takes `"default"`, `"priority"`, `"auto"`, or a duration `"HHh-MMm-SSs"` (5s-10m). The duration races OpenAI's flex tier on a flex-capable model and falls back to standard if it can't start in time; `"default"`, `"priority"`, and `"auto"` map to those OpenAI service tiers and proxy any model. It is typed, so a missing value or a value outside the allowed shapes is a **compile-time** error; a duration that fits the shape but is malformed or out of range is caught at **runtime**, the same check plain-JavaScript callers get. See the [docs](https://flexinference.mintlify.app/deadline-routing).
28
28
 
29
- ## Providers (OpenAI and Gemini)
29
+ ## Providers (OpenAI, Gemini, and Anthropic)
30
30
 
31
- FlexInference routes to **OpenAI** and **Google Gemini**. Send the same OpenAI-shaped request and pass whichever model id you want - `gpt-5.5`, `o4-mini`, `gemini-3.5-flash`, and so on. We run Gemini through its Interactions API and translate it back to the OpenAI shape, so your code is identical for both.
31
+ FlexInference routes to **OpenAI**, **Google Gemini**, and **Anthropic**. Send the same OpenAI-shaped request and pass whichever model id you want - `gpt-5.5`, `o4-mini`, `gemini-3.5-flash`, `claude-opus-4-8`, and so on. We translate Gemini and Anthropic to and from the OpenAI shape, so your code is identical for all three.
32
32
 
33
33
  - **OpenAI:** `default` (standard tier), `priority`, `auto`, and the flex race (a duration) on flex-capable models.
34
34
  - **Gemini:** `default` maps to Gemini's **standard** tier, plus `priority` and the flex race on the Gemini flex models (`gemini-3.5-flash`, `gemini-3.1-flash-lite`, `gemini-3.1-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`). Gemini has no `auto` tier, so `start_within: "auto"` on a Gemini model returns `400`.
35
+ - **Anthropic (Claude):** proxy-only. `default`, `priority`, and `auto` work; there is **no flex race**, so a duration `start_within` on a `claude-*` model returns `400 flex_unsupported_for_anthropic`. Anthropic requires a token cap, so set `max_output_tokens` (`max_completion_tokens` on Chat, `max_tokens` on Messages) or you get `400 missing_max_tokens`. You keep the unified API and tier control, and draw down your own Anthropic credits.
35
36
 
36
- Add the provider key you'll use (OpenAI and/or Gemini) in the [dashboard](https://www.flexinference.com/dashboard). Text, streaming, structured outputs, function calling, image input, and web search work on both providers (send a Responses `web_search` tool; we map it to Gemini's `google_search`).
37
+ Add the provider key you'll use (OpenAI, Gemini, and/or Anthropic) in the [dashboard](https://www.flexinference.com/dashboard). Text, streaming, structured outputs, function calling, image input, and web search work across providers (send a Responses `web_search` tool; we map it to Gemini's `google_search`).
37
38
 
38
39
  Don't send `service_tier` - the router controls the tier from `start_within` and rejects a caller-supplied `service_tier` with `400 service_tier_not_allowed`.
39
40
 
@@ -64,6 +65,39 @@ const res = await client.chat.completions.create({
64
65
  });
65
66
  ```
66
67
 
68
+ ## Interactions (Gemini shape)
69
+
70
+ Speak Google's Interactions shape and reach any model. `interactionText(res)` pulls the assistant text out of the interaction's `steps`.
71
+
72
+ ```ts
73
+ import { interactionText } from "flexinference";
74
+
75
+ const res = await client.interactions.create({
76
+ model: "gemini-3.5-flash",
77
+ input: "Summarize this contract.",
78
+ start_within: "00h-01m-00s",
79
+ });
80
+
81
+ console.log(interactionText(res));
82
+ ```
83
+
84
+ ## Messages (Anthropic shape)
85
+
86
+ Speak Anthropic's Messages shape and reach any model. `max_tokens` is required (Anthropic requires it). `messageText(res)` pulls the assistant text out of the message `content`.
87
+
88
+ ```ts
89
+ import { messageText } from "flexinference";
90
+
91
+ const res = await client.messages.create({
92
+ model: "claude-opus-4-8",
93
+ max_tokens: 1024,
94
+ messages: [{ role: "user", content: "Summarize this contract." }],
95
+ start_within: "default",
96
+ });
97
+
98
+ console.log(messageText(res));
99
+ ```
100
+
67
101
  ## Timeouts and cancellation
68
102
 
69
103
  Every client has a built-in timeout: **10 seconds to connect** and **600 seconds (10 min) total**, matching the router's budget, so a request can never hang forever. Override the total with the `timeout` option (raise it for very long streams):
@@ -102,6 +136,31 @@ try {
102
136
  }
103
137
  ```
104
138
 
139
+ ## Billing / 402
140
+
141
+ If your account's billing is past due, the router pauses **billable flex** and returns
142
+ `402 Payment Required` on those requests; free routing keeps working. The SDK throws a
143
+ typed `PaymentRequiredError` (a subclass of `FlexInferenceError`) for HTTP 402, so you
144
+ can catch it on its own and prompt the user to update payment while letting other errors
145
+ propagate:
146
+
147
+ ```ts
148
+ import { PaymentRequiredError } from "flexinference";
149
+
150
+ try {
151
+ await client.responses.create({ model: "gpt-5.5", input: "hi", start_within: "00h-00m-30s" });
152
+ } catch (err) {
153
+ if (err instanceof PaymentRequiredError) {
154
+ console.log("Billing is past due - update payment in the dashboard to resume flex.");
155
+ } else {
156
+ throw err;
157
+ }
158
+ }
159
+ ```
160
+
161
+ Because `PaymentRequiredError extends FlexInferenceError`, existing
162
+ `catch (err instanceof FlexInferenceError)` handlers keep catching 402s too.
163
+
105
164
  ## Configuration
106
165
 
107
166
  | Option | Default | Description |
package/dist/index.d.ts CHANGED
@@ -60,6 +60,7 @@ export interface FlexErrorBody {
60
60
  type: string;
61
61
  code?: string | null;
62
62
  param?: string | null;
63
+ doc_url?: string | null;
63
64
  };
64
65
  }
65
66
  export declare class FlexInferenceError extends Error {
@@ -67,8 +68,17 @@ export declare class FlexInferenceError extends Error {
67
68
  readonly type: string | undefined;
68
69
  readonly code: string | null | undefined;
69
70
  readonly param: string | null | undefined;
71
+ /** Deep link into the docs for this error's code, when the router supplied one. */
72
+ readonly docUrl: string | null | undefined;
70
73
  constructor(status: number, body: Partial<FlexErrorBody> | undefined, fallback: string);
71
74
  }
75
+ /**
76
+ * Thrown on HTTP 402: billing is past due, so billable flex is paused. Free routing
77
+ * still works. A subclass of FlexInferenceError, so existing `catch (err instanceof
78
+ * FlexInferenceError)` handlers keep working; narrow to this to handle 402 specially.
79
+ */
80
+ export declare class PaymentRequiredError extends FlexInferenceError {
81
+ }
72
82
  export interface ClientOptions {
73
83
  apiKey: string;
74
84
  /** Base URL of the FlexInference router. Defaults to the hosted endpoint. */
package/dist/index.js CHANGED
@@ -90,6 +90,8 @@ export class FlexInferenceError extends Error {
90
90
  type;
91
91
  code;
92
92
  param;
93
+ /** Deep link into the docs for this error's code, when the router supplied one. */
94
+ docUrl;
93
95
  constructor(status, body, fallback) {
94
96
  const err = body?.error;
95
97
  super(err?.message ?? fallback);
@@ -98,8 +100,16 @@ export class FlexInferenceError extends Error {
98
100
  this.type = err?.type;
99
101
  this.code = err?.code;
100
102
  this.param = err?.param;
103
+ this.docUrl = err?.doc_url;
101
104
  }
102
105
  }
106
+ /**
107
+ * Thrown on HTTP 402: billing is past due, so billable flex is paused. Free routing
108
+ * still works. A subclass of FlexInferenceError, so existing `catch (err instanceof
109
+ * FlexInferenceError)` handlers keep working; narrow to this to handle 402 specially.
110
+ */
111
+ export class PaymentRequiredError extends FlexInferenceError {
112
+ }
103
113
  const DEFAULT_BASE_URL = "https://api.flexinference.com/v1";
104
114
  const DEFAULT_TIMEOUT_MS = 600_000; // 10 min total budget, matching the router's generosity
105
115
  const CONNECT_TIMEOUT_MS = 10_000; // fail fast if the router is unreachable
@@ -117,16 +127,18 @@ function assertStartWithin(body) {
117
127
  ? body["start_within"]
118
128
  : undefined;
119
129
  if (value === undefined) {
120
- throw new Error(`FlexInference: \`start_within\` is required. Set it to ${START_WITHIN_VALUES}.`);
130
+ throw new Error(`FlexInference: \`start_within\` is required on every request. Set it to ${START_WITHIN_VALUES}, ` +
131
+ `for example "start_within": "default" for standard-tier behavior or "00h-00m-30s" for a 30-second flex race.`);
121
132
  }
122
133
  if (typeof value !== "string") {
123
- throw new Error(`FlexInference: \`start_within\` must be a string (${START_WITHIN_VALUES}).`);
134
+ throw new Error(`FlexInference: \`start_within\` must be a string (${START_WITHIN_VALUES}), for example "default".`);
124
135
  }
125
136
  if (value === "default" || value === "priority" || value === "auto")
126
137
  return;
127
138
  const match = DURATION_RE.exec(value);
128
139
  if (match === null) {
129
- throw new Error(`FlexInference: \`start_within\` "${value}" is not ${START_WITHIN_VALUES}.`);
140
+ throw new Error(`FlexInference: \`start_within\` "${value}" is not ${START_WITHIN_VALUES}. ` +
141
+ `A duration is two digits each for hours, minutes, and seconds, for example "00h-05m-00s" for a 5-minute deadline.`);
130
142
  }
131
143
  const seconds = Number(match[1]) * SECONDS_PER_HOUR + Number(match[2]) * SECONDS_PER_MINUTE + Number(match[3]);
132
144
  if (seconds < MIN_DEADLINE_SECONDS || seconds > MAX_DEADLINE_SECONDS) {
@@ -218,6 +230,9 @@ export class FlexInference {
218
230
  finally {
219
231
  cleanup();
220
232
  }
233
+ if (res.status === 402) {
234
+ throw new PaymentRequiredError(res.status, parsed, `HTTP ${String(res.status)} ${res.statusText}`);
235
+ }
221
236
  throw new FlexInferenceError(res.status, parsed, `HTTP ${String(res.status)} ${res.statusText}`);
222
237
  }
223
238
  return { res, cleanup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexinference",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Official TypeScript SDK for FlexInference - a deadline-aware, OpenAI-compatible inference router.",
5
5
  "license": "MIT",
6
6
  "author": "Aditya Perswal <adityaperswal@gmail.com>",