pi-braid-providers 1.0.6 → 1.0.7
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 +68 -217
- package/build-info.json +4 -4
- package/index.ts +25 -16
- package/package.json +12 -11
- package/presend-guard.ts +344 -73
- package/ring-lifecycle.ts +355 -26
package/README.md
CHANGED
|
@@ -1,243 +1,94 @@
|
|
|
1
1
|
# pi-braid-providers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Key rotation, provider failover, and route health for the [pi](https://github.com/earendil-works/pi)
|
|
4
|
+
coding agent: several keys per provider, a dead key retired instead of retried, and a failover to a
|
|
5
|
+
healthier route when a provider's ring runs dry.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Additional LLM providers for `pi-coding-agent`, built on a generalized **API key ring** for multi-key rotation and self-healing.
|
|
10
|
-
|
|
11
|
-
The `providers` workspace registers providers into pi at session start. The key-ring machinery that both the agent loop and direct extension LLM calls use ships here; the provider itself is the **`agent-zero`** setup: the multi-vendor gateway that proxies Venice's models, configured from a catalog file resolved at runtime.
|
|
12
|
-
|
|
13
|
-
> **A fresh install registers no provider.** The `agent-zero` model catalog
|
|
14
|
-
> (Venice's models) and gateway URL are read from a userland catalog file
|
|
15
|
-
> resolved at runtime (see `venice-catalog.ts`) that this package does not
|
|
16
|
-
> include. Without it the extension loads with the key-ring machinery active
|
|
17
|
-
> and zero registered providers; a synthetic route can be registered through
|
|
18
|
-
> `registerRoute` (`ring-lifecycle.ts`), the seam the test suite uses for its
|
|
19
|
-
> multi-route coverage.
|
|
20
|
-
|
|
21
|
-
## The key ring
|
|
22
|
-
|
|
23
|
-
Each provider gets its own key ring with **exponential backoff**:
|
|
24
|
-
|
|
25
|
-
- **Multi-key rotation.** Keys load from `${PREFIX}_P0` … `${PREFIX}_P9` in the
|
|
26
|
-
environment, falling back to the bare `PREFIX` var when no numbered keys
|
|
27
|
-
exist. On a quota/auth/403 error the active key is exhausted and the next
|
|
28
|
-
request serves the next healthy key.
|
|
29
|
-
- **Persisted state.** Exhausted-key state persists to
|
|
30
|
-
`~/.pi/agent/.key-ring-state-<provider>.json`, so restarts skip known-dead
|
|
31
|
-
keys instead of retrying them and hitting the same 403. The file lives in
|
|
32
|
-
pi's agent directory (`state-paths.ts` owns the resolution); if no absolute
|
|
33
|
-
home can be resolved, the ring refuses to persist and says so rather than
|
|
34
|
-
writing a path relative to the cwd.
|
|
35
|
-
- **Self-healing turn retry.** When a turn fails and a fresh key remains, an
|
|
36
|
-
explicit marker is appended so pi-core re-runs the turn on the next key
|
|
37
|
-
automatically. Fatal account-level errors (quota exhausted, billing, balance)
|
|
38
|
-
are never masqueraded as retryable.
|
|
39
|
-
- **Direct-call feedback.** Direct extension LLM calls (an extension's own
|
|
40
|
-
`completeSimple`/`streamSimple` use)
|
|
41
|
-
feed 429/403 back into the ring too, so no dead key is served on those paths.
|
|
42
|
-
- **Diagnostics.** Rotation/exhaustion events route through a throttled notify
|
|
43
|
-
sink (not `console.*`); a loud _all-keys-exhausted_ warning fires when every
|
|
44
|
-
key is in backoff.
|
|
45
|
-
|
|
46
|
-
## Installation
|
|
7
|
+
## Install
|
|
47
8
|
|
|
48
9
|
```bash
|
|
49
10
|
pi install npm:pi-braid-providers
|
|
50
11
|
```
|
|
51
12
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"packages": ["file:~/pi-braid/packages/providers"]
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Pre-send payload guard
|
|
13
|
+
Then name your routes (see Configuration). Until a route and its key are configured, the extension
|
|
14
|
+
loads the machinery and registers no provider: inert, not broken.
|
|
61
15
|
|
|
62
|
-
|
|
63
|
-
| ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
64
|
-
| `braid.context.presendGuard.enabled` | `true` | When enabled, if an estimated request exceeds 90% of a known model context window, oversized strings that can be flushed are written losslessly to disk and replaced by stubs. `false` skips the guard; unknown windows are not guessed. | Re-read at use time for every provider request. |
|
|
16
|
+
## What you get
|
|
65
17
|
|
|
66
|
-
|
|
18
|
+
| Surface | What it gives you |
|
|
19
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| Multi-key rotation | Numbered keys per provider, falling back to the bare variable. On a quota, auth or 403 error the active key is retired and the next request uses the next healthy one |
|
|
21
|
+
| Persisted state | Key-ring state on disk, so a restart skips keys already known dead instead of retrying the same 403 |
|
|
22
|
+
| Turn retry | When a turn fails and a fresh key remains, the next key serves the retry; fatal account errors are never disguised as retryable |
|
|
23
|
+
| Failover | An ordered route table, walked one rotation per episode, with one notice to the owner and a bounded fail-back probe that returns when the dead route recovers |
|
|
24
|
+
| Route health | `ring_health` reports each ring and each route's last probe: read-only, key hashes only, never writes |
|
|
25
|
+
| Request guard | `braid.context.presendGuard` flushes oversized strings to disk above 90% of a known window, so a large request shrinks instead of being rejected |
|
|
67
26
|
|
|
68
|
-
|
|
69
|
-
catalog file resolved at runtime; without it the extension loads the key-ring machinery and
|
|
70
|
-
registers zero providers.
|
|
71
|
-
- It does not rotate to a route you did not configure - a route is recognized only by the model ids
|
|
72
|
-
it declares.
|
|
73
|
-
- It does not retry a fatal account error (quota exhausted, billing, balance) as if it were transient.
|
|
27
|
+
## Configuration
|
|
74
28
|
|
|
75
|
-
|
|
29
|
+
### Routes
|
|
76
30
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
31
|
+
`braid.providers.routes` is an ordered array; array order is rotation order. Each entry names the
|
|
32
|
+
provider id pi will expose, the environment variable prefix its keys load from, and optionally the
|
|
33
|
+
model ids it serves and a non-billable status probe:
|
|
80
34
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"braid": {
|
|
38
|
+
"providers": {
|
|
39
|
+
"routes": [
|
|
40
|
+
{
|
|
41
|
+
"provider": "venice-direct",
|
|
42
|
+
"keys": "VENICE_API_KEY",
|
|
43
|
+
"probe": {
|
|
44
|
+
"url": "https://api.venice.ai/api/v1/api_keys/rate_limits",
|
|
45
|
+
"balanceFloorUsd": 1
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
88
52
|
```
|
|
89
53
|
|
|
90
|
-
|
|
91
|
-
`
|
|
92
|
-
|
|
54
|
+
A route is recognized by the model ids it declares, so a route you did not configure is never
|
|
55
|
+
routed to. With no `routes` key, the two routes this build carries are used in order: the gateway
|
|
56
|
+
route, whose model catalog comes from a userland file that this package does not ship, and
|
|
57
|
+
`venice-direct`, which serves Venice's own API. Without that catalog the gateway route does not
|
|
58
|
+
register, and only routes you configure serve traffic. A route's `probe` declares a status
|
|
59
|
+
endpoint that is checked with the route's own key, at registration and on a bounded interval
|
|
60
|
+
afterwards; the check is never a billable call, and its result steers routing only while it is
|
|
61
|
+
fresher than `braid.providers.probeFreshnessMs`.
|
|
93
62
|
|
|
94
|
-
|
|
63
|
+
### Settings
|
|
95
64
|
|
|
96
|
-
|
|
65
|
+
| Key | Default | Effect |
|
|
66
|
+
| ------------------------------------ | --------------------- | --------------------------------------------------------------------------------- |
|
|
67
|
+
| `braid.providers.routes` | the pair above | Ordered route table; a configured array replaces the default pair entirely |
|
|
68
|
+
| `braid.providers.probeFreshnessMs` | `900000` (15 minutes) | How long a recorded probe or death may steer routing; `0` disables probe steering |
|
|
69
|
+
| `braid.context.presendGuard.enabled` | `true` | The 90% request guard described above |
|
|
97
70
|
|
|
98
|
-
|
|
99
|
-
| --------------- | ------------------ | --------------------------------------------------- | -------------------- |
|
|
100
|
-
| `agent-zero` | Venice's models | agent-zero gateway (`https://llm.agent-zero.ai/v1`) | `AGENT_ZERO_API_KEY` |
|
|
101
|
-
| `venice-direct` | The same model ids | Venice's own API (`https://api.venice.ai/api/v1`) | `VENICE_API_KEY` |
|
|
71
|
+
### Environment
|
|
102
72
|
|
|
103
|
-
`
|
|
73
|
+
`AGENT_ZERO_API_KEY` for the gateway route, `VENICE_API_KEY` for the direct route; numbered
|
|
74
|
+
suffixes (`_P0` through `_P9`) add rotation keys, and the bare variable is used when no numbered
|
|
75
|
+
key exists.
|
|
104
76
|
|
|
105
|
-
##
|
|
77
|
+
## Limits
|
|
106
78
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`opencode-go` sits ahead of the gateway in the live table, so a session that
|
|
114
|
-
starts there leaves by the health walk instead of by hand. The entry declares
|
|
115
|
-
no context window, max tokens or input types (pi reads those from the physical
|
|
116
|
-
model that answered) and never dispatches its own id: a request with no
|
|
117
|
-
previous physical model resolves its base id from the route table, or from the
|
|
118
|
-
configured `defaultModel`, and the thinking level is resolved against the
|
|
119
|
-
target model's own map. A provider that already ships a physical `auto` skips
|
|
120
|
-
its own entry with one diagnostic, so a virtual entry can never shadow a real
|
|
121
|
-
model.
|
|
79
|
+
- It is not a proxy or a gateway you can point other tools at: it plugs into pi's model registry.
|
|
80
|
+
- A route you did not configure is never routed to.
|
|
81
|
+
- Fatal account errors (quota, billing, balance) stop the turn rather than rotate.
|
|
82
|
+
- Ring and route state live directly under `~/.pi/agent/`, not under `braid/`:
|
|
83
|
+
`.key-ring-state-<provider>.json` and `.provider-route-state.json`. Key material is never
|
|
84
|
+
written, only key hashes.
|
|
122
85
|
|
|
123
|
-
##
|
|
124
|
-
|
|
125
|
-
A route may declare a non-billable status capability in the ordered route table:
|
|
126
|
-
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"provider": "venice-direct",
|
|
130
|
-
"keys": "VENICE_API_KEY",
|
|
131
|
-
"probe": {
|
|
132
|
-
"url": "https://api.venice.ai/api/v1/api_keys/rate_limits",
|
|
133
|
-
"balanceFloorUsd": 1
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
86
|
+
## For maintainers
|
|
137
87
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
`~/.pi/agent/.provider-route-state.json`: the last probe, the last recorded
|
|
142
|
-
death (its key identity as a hash, never the key), the fail-back target and the
|
|
143
|
-
warmed model ids. A later session reads that file instead of re-learning it.
|
|
144
|
-
The probe never throws, never rotates a key and never starts a billable call;
|
|
145
|
-
the write is lock- and CAS-guarded exactly like the key-ring state, because
|
|
146
|
-
every pi process on the host shares the file.
|
|
147
|
-
|
|
148
|
-
A recorded outcome carries the endpoint's own remaining figure as
|
|
149
|
-
`remainingUsd` (Venice's `balances.USD`, OpenRouter's `limit_remaining`), so no
|
|
150
|
-
consumer has to derive remaining by subtracting a monthly figure from a cap. A
|
|
151
|
-
route whose status document reports a spendable USD balance is classified
|
|
152
|
-
against `probe.balanceFloorUsd` (default `1`, owner-configurable per route):
|
|
153
|
-
above the floor is `ok`, at or below it but above zero is `degraded`, and zero
|
|
154
|
-
or below is `unavailable`. A route whose status endpoint answers 200 while the
|
|
155
|
-
account has no spendable balance therefore no longer reads as healthy.
|
|
156
|
-
|
|
157
|
-
Two rules keep the layers honest:
|
|
158
|
-
|
|
159
|
-
- **The runner is provider-agnostic.** Which provider ids exist and what their
|
|
160
|
-
status responses look like lives in ONE adapter table in `probe.ts`. A
|
|
161
|
-
provider absent from it is reported unavailable rather than guessed at.
|
|
162
|
-
- **The state store interprets nothing.** It writes down an opaque record per
|
|
163
|
-
route and reads it back; the probe vocabulary and the death classes belong to
|
|
164
|
-
the layers that produce them.
|
|
165
|
-
|
|
166
|
-
A route without a `probe` descriptor is never probed. Absent probes, resolution
|
|
167
|
-
and rotation are unchanged.
|
|
168
|
-
|
|
169
|
-
## Ring and route health
|
|
170
|
-
|
|
171
|
-
`ring-health.ts` is the read side of those two files: it summarizes them for a
|
|
172
|
-
human or a consumer script without writing, locking or interpreting anything, so
|
|
173
|
-
"which keys are in backoff, and what did each route last look like" needs no
|
|
174
|
-
knowledge of either store's write protocol.
|
|
175
|
-
|
|
176
|
-
- `ringHealth(providerId, { stateDir?, file? })` reads one ring file
|
|
177
|
-
(`~/.pi/agent/.key-ring-state-<provider>.json`, or an explicit path).
|
|
178
|
-
`{ provider, present: false }` means no file, or one this reader cannot
|
|
179
|
-
trust; otherwise it carries `rev`, `savedAt`, `activeIndex` and one entry per
|
|
180
|
-
key: `{ exhausted, exhaustedAt, retryCount, exhaustedBy, keyHash }`.
|
|
181
|
-
- `routeHealth({ stateDir?, providers?, routeState? })` reports every route it
|
|
182
|
-
can see - the ids the caller names, the routes the route store already
|
|
183
|
-
carries, and the providers with a ring file - as
|
|
184
|
-
`{ provider, ring, lastProbe, deathClass, deathAt, failback, warmModels }`.
|
|
185
|
-
Facts the route store has not recorded come back as `null`, `0` or `[]`.
|
|
186
|
-
|
|
187
|
-
Four rules hold the reader to its layer:
|
|
188
|
-
|
|
189
|
-
- **Read-only.** No write, no lock, no migration. The stores own the one write
|
|
190
|
-
protocol; a reader that could write would be a second one.
|
|
191
|
-
- **Hash-only.** The ring file holds `hash`, never the key, and the reader
|
|
192
|
-
copies named fields instead of forwarding the document - so a value the ring
|
|
193
|
-
never wrote cannot leak through here.
|
|
194
|
-
- **Provider-blind.** Every provider id is an argument or is discovered from
|
|
195
|
-
the files; none is hardcoded, so a configured route table needs no code
|
|
196
|
-
change here.
|
|
197
|
-
- **Tolerant, never throwing.** An absent file is the normal empty state and
|
|
198
|
-
stays silent; a malformed, partial or wrong-version file is the same
|
|
199
|
-
`present: false` plus exactly one diagnostic.
|
|
200
|
-
|
|
201
|
-
The reader takes its file paths from its caller or derives them from the
|
|
202
|
-
provider id; it writes no state file of its own and reads the route document
|
|
203
|
-
through `route-state.ts`'s reader. Nothing in `packages/core` imports it: an
|
|
204
|
-
external consumer imports the package, never the other way round.
|
|
205
|
-
|
|
206
|
-
`formatRingHealth(routes, freshness)` and `summarizeRouteHealth(routes, freshness)`
|
|
207
|
-
turn a report into the `ring_health` tool's text and its `details` payload.
|
|
208
|
-
|
|
209
|
-
- `braid.providers.probeFreshnessMs` (default `900000` ms, 15 minutes) controls
|
|
210
|
-
how long a recorded unavailable probe or shape-death result can steer routing.
|
|
211
|
-
`0` or less disables probe steering. The ring-health renderers receive the
|
|
212
|
-
same resolved window and mark a death `(expired)` when the router's
|
|
213
|
-
`deathInForce` predicate would no longer act on it, its timestamp is absent,
|
|
214
|
-
or the window is off.
|
|
215
|
-
|
|
216
|
-
## Disabling the provider
|
|
217
|
-
|
|
218
|
-
The registration lives in `index.ts` behind the venice-catalog seam: remove
|
|
219
|
-
(or chmod) the userland catalog file and no provider is registered. All
|
|
220
|
-
ring-lifecycle logic stays in `ring-lifecycle.ts`, unit-testable without
|
|
221
|
-
loading pi-ai.
|
|
222
|
-
|
|
223
|
-
## Package layout
|
|
88
|
+
The route table internals, the probe adapter table, the state store protocol and the failure
|
|
89
|
+
classes are in [AGENTS.md](https://github.com/kifirkin/pi-braid/blob/main/packages/providers/AGENTS.md),
|
|
90
|
+
which the tarball does not carry.
|
|
224
91
|
|
|
225
|
-
|
|
226
|
-
providers/
|
|
227
|
-
├── index.ts # registers the configured routes + wires key-ring/rotation events
|
|
228
|
-
├── venice-catalog.ts # userland catalog seam (the models file, resolved at runtime)
|
|
229
|
-
├── venice-direct.ts # the venice-direct route module: constants, key ring, model configs
|
|
230
|
-
├── key-ring.ts # multi-key rotation + backoff + persistence
|
|
231
|
-
├── ring-lifecycle.ts # per-request ring attribution, exhaust/self-heal, route-order rotation engine
|
|
232
|
-
├── probe.ts # provider-agnostic probe runner + the adapter table
|
|
233
|
-
├── route-state.ts # cross-session route state, lock + CAS writes
|
|
234
|
-
├── ring-health.ts # read-only health surface over the key-ring + route state files
|
|
235
|
-
├── log.ts # notify-sink routing (no console.*)
|
|
236
|
-
└── test/ # fixture-driven behavior pins
|
|
237
|
-
```
|
|
92
|
+
## License
|
|
238
93
|
|
|
239
|
-
|
|
240
|
-
> follows the gateway the provider serves models through. `venice-direct`, the
|
|
241
|
-
> `VENICE_API_KEY` family and `venice-models.json` are unchanged; the gateway's
|
|
242
|
-
> own credential namespace moved from `A0_VENICE_API_KEY` to `AGENT_ZERO_API_KEY`
|
|
243
|
-
> with the last rename.
|
|
94
|
+
[MIT](LICENSE)
|
package/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-braid-providers",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"sha": "
|
|
5
|
-
"shaShort": "
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"sha": "3a3de965131cd125b58a807b064a43d37b7f466f",
|
|
5
|
+
"shaShort": "3a3de965",
|
|
6
6
|
"branch": "detached",
|
|
7
7
|
"dirty": false,
|
|
8
|
-
"packedAt": "2026-
|
|
8
|
+
"packedAt": "2026-11-11T22:45:21.903Z"
|
|
9
9
|
}
|
package/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { createProvider } from "@earendil-works/pi-ai";
|
|
3
|
-
// `Type`
|
|
4
|
-
//
|
|
5
|
-
|
|
3
|
+
// pi-ai re-exported typebox's `Type` until 1.5.0 and no longer does, so the
|
|
4
|
+
// builder comes from the package that owns it, at the peer floor the sibling
|
|
5
|
+
// packages declare.
|
|
6
|
+
import { Type } from "typebox";
|
|
6
7
|
import {
|
|
7
8
|
completeSimple,
|
|
8
9
|
openAICompletionsApi,
|
|
@@ -48,7 +49,11 @@ import {
|
|
|
48
49
|
import { loadVeniceCatalog } from "./venice-catalog.js";
|
|
49
50
|
import type { ChatModelConfig } from "./venice-catalog.js";
|
|
50
51
|
import { keyRingAuth, withKeyRingAuth } from "./ring-provider";
|
|
51
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
buildContextWindowIndex,
|
|
54
|
+
readPresendPolicy,
|
|
55
|
+
shrinkDoomedPayload,
|
|
56
|
+
} from "./presend-guard";
|
|
52
57
|
import { probeRoute } from "./probe.js";
|
|
53
58
|
import {
|
|
54
59
|
formatRingHealth,
|
|
@@ -850,25 +855,29 @@ export default async function (pi: ExtensionAPI) {
|
|
|
850
855
|
}
|
|
851
856
|
|
|
852
857
|
// Model-id → catalog context window for the pre-send guard.
|
|
853
|
-
// Built once at factory load from the catalog this package owns
|
|
854
|
-
|
|
855
|
-
for
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
858
|
+
// Built once at factory load from the catalog this package owns
|
|
859
|
+
// (venice-catalog.ts): exact ids, plus the normalized spelling a route may
|
|
860
|
+
// use for the same model. A dot-form request id (the route sends
|
|
861
|
+
// `deepseek-v4.1-flash`, the catalog spells `deepseek-v4-1-flash`) would
|
|
862
|
+
// otherwise resolve to no window and the guard would silently no-op.
|
|
863
|
+
const presendWindows = buildContextWindowIndex(veniceCatalog?.models ?? []);
|
|
860
864
|
|
|
861
865
|
pi.on("before_provider_request", async (event: any) => {
|
|
862
866
|
// Ring-lifecycle first (WAF/key shaping), then the pre-send guard on
|
|
863
|
-
// the (possibly reshaped) payload. The
|
|
864
|
-
// DOOMED for a KNOWN model;
|
|
865
|
-
// the
|
|
867
|
+
// the (possibly reshaped) payload. The token arm no-ops unless the payload
|
|
868
|
+
// is DOOMED for a KNOWN model; the byte arm no-ops unless the owner
|
|
869
|
+
// declared a cap (a request above the report threshold still writes one
|
|
870
|
+
// line). Its returned payload (when present) replaces the outgoing body
|
|
871
|
+
// per pi-core's emitBeforeProviderRequest semantics.
|
|
866
872
|
const shaped = handleBeforeProviderRequest(event.payload);
|
|
867
873
|
const outgoing = shaped ?? event.payload;
|
|
868
874
|
try {
|
|
869
|
-
|
|
875
|
+
const policy = readPresendPolicy();
|
|
876
|
+
if (!policy.enabled) return shaped;
|
|
870
877
|
const guard = shrinkDoomedPayload(outgoing, {
|
|
871
|
-
resolveContextWindow: (modelId) =>
|
|
878
|
+
resolveContextWindow: (modelId) => presendWindows.resolve(modelId),
|
|
879
|
+
maxRequestBytes: policy.maxRequestBytes,
|
|
880
|
+
reportBytesAbove: policy.reportBytesAbove,
|
|
872
881
|
});
|
|
873
882
|
return guard?.payload ?? shaped;
|
|
874
883
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-braid-providers",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "Key rotation, provider failover, and route health for the pi coding agent, so one dead key or provider does not end the session.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"*.ts",
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"pi-package",
|
|
17
|
-
"
|
|
17
|
+
"braid",
|
|
18
18
|
"pi-coding-agent",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"venice
|
|
19
|
+
"llm-provider",
|
|
20
|
+
"api-key-rotation",
|
|
21
|
+
"failover",
|
|
22
|
+
"venice"
|
|
23
23
|
],
|
|
24
24
|
"pi": {
|
|
25
25
|
"extensions": [
|
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "6.0.2",
|
|
41
|
-
"@earendil-works/pi-ai": "^1.
|
|
41
|
+
"@earendil-works/pi-ai": "^1.5.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@earendil-works/pi-ai": "^1.
|
|
45
|
-
"@earendil-works/pi-coding-agent": "^1.
|
|
44
|
+
"@earendil-works/pi-ai": "^1.5.0",
|
|
45
|
+
"@earendil-works/pi-coding-agent": "^1.5.0",
|
|
46
|
+
"typebox": "^1.1.38"
|
|
46
47
|
},
|
|
47
48
|
"author": "kifirkin",
|
|
48
49
|
"license": "MIT",
|
|
@@ -56,6 +57,6 @@
|
|
|
56
57
|
},
|
|
57
58
|
"homepage": "https://github.com/kifirkin/pi-braid/tree/main/packages/providers",
|
|
58
59
|
"engines": {
|
|
59
|
-
"node": ">=
|
|
60
|
+
"node": ">=24.0.0"
|
|
60
61
|
}
|
|
61
62
|
}
|