pi-freeflow 1.15.1 → 1.16.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 +13 -0
- package/package.json +1 -1
- package/src/catalog.ts +2 -2
- package/src/index.ts +1 -1
- package/src/models.ts +16 -3
- package/src/proxy.ts +23 -2
- package/src/relay-state.ts +1133 -1133
- package/src/relay.ts +399 -404
- package/src/types.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- New free model: Union Alpha Free joins the picker. It answers through a different upstream endpoint than the other free models, handled automatically — 262K context, image input, tool calling, and zero cost like the rest of the catalog.
|
|
8
|
+
|
|
9
|
+
## 1.15.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 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.
|
|
14
|
+
- 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.
|
|
15
|
+
|
|
3
16
|
## 1.15.1
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-freeflow",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.16.0",
|
|
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/catalog.ts
CHANGED
|
@@ -77,7 +77,7 @@ export function sanitizeCatalogModels(models: RegisteredModel[]): RegisteredMode
|
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
79
|
* In-memory cache of currently active/available free models.
|
|
80
|
-
* Initialized with all
|
|
80
|
+
* Initialized with all 27 verified models for 0ms instant availability.
|
|
81
81
|
*/
|
|
82
82
|
let aliveCatalog: RegisteredModel[] = ALL_MODELS.map((m) => ({
|
|
83
83
|
...m,
|
|
@@ -400,6 +400,6 @@ export async function refreshCatalog(force = false): Promise<RegisteredModel[]>
|
|
|
400
400
|
} catch (err) {
|
|
401
401
|
logDebug("Failed reading stale catalog cache", { error: String(err) });
|
|
402
402
|
}
|
|
403
|
-
// No valid cache — return in-memory static
|
|
403
|
+
// No valid cache — return in-memory static 27 (host will refresh if needed)
|
|
404
404
|
return aliveCatalog;
|
|
405
405
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-freeflow — Modular, high-resiliency LLM extension for Pi & Oh My Pi (OMP)
|
|
3
3
|
*
|
|
4
|
-
* Provides access to
|
|
4
|
+
* Provides access to 27 free models (8 OpenCode Zen + 19 KiloCode Gateway) with:
|
|
5
5
|
* - Single-port daemon reuse on 28180 across concurrent subagents
|
|
6
6
|
* - Multi-cloud rolling egress relays (Vercel Edge, Cloudflare, Deno)
|
|
7
7
|
* - 0ms instant startup with verified static catalog and background live health checks
|
package/src/models.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static model definitions and upstream routing catalogs for pi-freeflow
|
|
3
3
|
*
|
|
4
|
-
* Defines the
|
|
5
|
-
* -
|
|
4
|
+
* Defines the 27 verified free models:
|
|
5
|
+
* - 8 OpenCode Zen models (2 Responses API + 5 Chat Completions + 1 Anthropic Messages)
|
|
6
6
|
* - 19 KiloCode Keyless Gateway models (18 OpenRouter format + 1 Standard format)
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -127,6 +127,19 @@ export const OPENCODE_MODELS: ModelDef[] = [
|
|
|
127
127
|
xhigh: "xhigh",
|
|
128
128
|
max: null,},
|
|
129
129
|
},
|
|
130
|
+
{
|
|
131
|
+
id: "union-alpha",
|
|
132
|
+
name: "Union Alpha Free",
|
|
133
|
+
reasoning: true,
|
|
134
|
+
contextWindow: 262_144,
|
|
135
|
+
maxTokens: 131_072,
|
|
136
|
+
api: "anthropic-messages",
|
|
137
|
+
input: ["text", "image"],
|
|
138
|
+
// No thinkingLevelMap: the effort wire values for this endpoint are
|
|
139
|
+
// unknown, so the host sends no effort param. The model answers
|
|
140
|
+
// anyway (proven live 2026-09-17: POST /zen/v1/messages streams real
|
|
141
|
+
// tokens at zero cost). Followup: probe effort values, then add a map.
|
|
142
|
+
},
|
|
130
143
|
];
|
|
131
144
|
|
|
132
145
|
/**
|
|
@@ -389,7 +402,7 @@ export const KILO_MODEL_IDS = new Set<string>([
|
|
|
389
402
|
]);
|
|
390
403
|
|
|
391
404
|
/**
|
|
392
|
-
* Combined list of all
|
|
405
|
+
* Combined list of all 27 static free models (canonical)
|
|
393
406
|
*/
|
|
394
407
|
export const ALL_MODELS: ModelDef[] = [...OPENCODE_MODELS, ...KILO_MODELS];
|
|
395
408
|
|
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
|
|
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
|
|
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
|