pi-freeflow 1.15.1 → 1.15.2

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.15.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Relay traffic is now spent only on the free models the pool exists for: requests for other models go straight upstream even with the relay pool on, cutting relay bandwidth. Applies in automatic and always-on modes; relay-off behavior is unchanged.
8
+ - The disabled-deployment rollover now triggers only on genuine hosting verdicts, so ordinary payment and quota refusals always surface immediately without cooling a healthy relay. Relay-list imports also no longer show a negative removal count when the file holds more relays than the current pool.
9
+
3
10
  ## 1.15.1
4
11
 
5
12
  ### Patch Changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-freeflow",
3
3
  "type": "module",
4
- "version": "1.15.1",
4
+ "version": "1.15.2",
5
5
  "description": "Thin provider for OMP/Pi — model list + dumb relay proxy + log; host pi-ai owns thinking/normalization",
6
6
  "main": "extensions/index.ts",
7
7
  "types": "src/index.ts",
package/src/proxy.ts CHANGED
@@ -89,6 +89,21 @@ function withRateLimitHint(status: number, data: string): string {
89
89
  return data;
90
90
  }
91
91
 
92
+ /**
93
+ * Relay eligibility: only free-catalog models ride relay egress. Returns true
94
+ * when the model is a non-empty string present in MODEL_MAP or KILO_MODEL_IDS
95
+ * (Kilo traffic leaves via its own branch regardless), and when the model is
96
+ * missing/empty (e.g. GET /v1/models carries no body — preserve routing).
97
+ * Anything else (unknown/paid model ids, non-strings) goes direct upstream.
98
+ */
99
+ export function isRelayEligibleModel(model: unknown): boolean {
100
+ if (model === undefined || model === null) return true;
101
+ if (typeof model !== "string") return false;
102
+ if (model.trim() === "") return true;
103
+ const canonical = resolveCanonicalModelId(model.trim());
104
+ return MODEL_MAP.has(canonical) || KILO_MODEL_IDS.has(canonical);
105
+ }
106
+
92
107
  /**
93
108
  * Extract client IP address from incoming HTTP request.
94
109
  */
@@ -692,12 +707,18 @@ export function startProxy(
692
707
  res.end(data);
693
708
  }
694
709
  } else {
695
- // OpenCode routing — relay when enabled, else direct upstream
710
+ // OpenCode routing — relay egress is for free-catalog models
711
+ // only; unknown/paid models go direct upstream.
696
712
  const relayState = getActiveRelayState();
697
- const shouldUseRelay =
713
+ const relayPoolOn =
698
714
  relayState.mode !== "off" &&
699
715
  relayState.enabled !== false &&
700
716
  Boolean(relayState.url || (relayState.relays && relayState.relays.length > 0));
717
+ const relayEligible = isRelayEligibleModel(parsedBody?.model);
718
+ const shouldUseRelay = relayPoolOn && relayEligible;
719
+ if (relayPoolOn && !relayEligible) {
720
+ log("info", `relay bypass for non-catalog model ${String(parsedBody?.model ?? "?")} — routing direct upstream`, { model: parsedBody?.model }, reqId);
721
+ }
701
722
  if (shouldUseRelay) {
702
723
  const fullUrl = `${UPSTREAM_OPENCODE}${req.url ?? "/"}`;
703
724
  const activeHost = relayState.url