@pwguler/pi-pengepul-provider 0.2.4 → 0.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/README.md +50 -12
- package/extensions/api-key.ts +6 -37
- package/extensions/config.ts +15 -21
- package/extensions/credential.ts +113 -0
- package/extensions/index.ts +22 -54
- package/extensions/models.ts +229 -165
- package/extensions/provider.ts +269 -0
- package/package.json +2 -2
- package/extensions/runtime.ts +0 -211
package/README.md
CHANGED
|
@@ -41,8 +41,8 @@ prefixed `pengepul/<id>`. To try it without installing, use
|
|
|
41
41
|
|
|
42
42
|
## What it does
|
|
43
43
|
|
|
44
|
-
- Registers the `pengepul` provider against
|
|
45
|
-
(`http://127.0.0.1:8317` by default).
|
|
44
|
+
- Registers the `pengepul` provider against the relay base URL on your
|
|
45
|
+
credential (`http://127.0.0.1:8317` by default).
|
|
46
46
|
- Discovers models from `GET /v1/models`, maps each to the right wire:
|
|
47
47
|
- `claude-*` / `anthropic/*` and `owned_by: anthropic` → Anthropic Messages
|
|
48
48
|
(`POST /v1/messages`),
|
|
@@ -53,24 +53,60 @@ prefixed `pengepul/<id>`. To try it without installing, use
|
|
|
53
53
|
`max_output_tokens`, `input_modalities`, `pricing`). Fields the relay omits
|
|
54
54
|
fall back to pi's builtin catalog for the same id, then to family
|
|
55
55
|
heuristics.
|
|
56
|
-
-
|
|
57
|
-
|
|
56
|
+
- Names the extended thinking levels (`xhigh`, `max`) that a model's own family
|
|
57
|
+
publishes. An id the catalogs have not caught up with — a point release the
|
|
58
|
+
relay already serves — takes them from the same family's previous minor, so
|
|
59
|
+
it does not sit at `high` until the next pi catalog update. Nothing to
|
|
60
|
+
configure: `thinkingLevelMap` is resolved per id here.
|
|
61
|
+
- Caches the catalog in pi's model store, so startup does not wait on the
|
|
62
|
+
network and a briefly absent relay is covered. The cached models are
|
|
63
|
+
re-pointed at the relay base configured now, so moving the relay does not
|
|
64
|
+
leave requests aimed at the old address.
|
|
58
65
|
- Reuses pi's built-in stream functions for both wires — no custom transport.
|
|
59
|
-
- Registers no commands: the catalog
|
|
66
|
+
- Registers no commands: pi refreshes the catalog on every startup.
|
|
60
67
|
|
|
61
68
|
## Configuration
|
|
62
69
|
|
|
70
|
+
Everything lives in `~/.pi/agent/auth.json`: the relay's API key and the relay
|
|
71
|
+
base it applies to.
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"pengepul": {
|
|
76
|
+
"type": "api_key",
|
|
77
|
+
"key": "sk-local-...",
|
|
78
|
+
"baseUrl": "http://127.0.0.1:8317"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`/login pengepul` writes both fields for you — it asks for the key, then for the
|
|
84
|
+
relay URL, and defaults the URL to `http://127.0.0.1:8317`. Editing the file by
|
|
85
|
+
hand works the same way; `baseUrl` may end in `/v1` or not.
|
|
86
|
+
|
|
87
|
+
To reach a relay on another machine, put that machine's address in `baseUrl`
|
|
88
|
+
(and make the relay listen beyond loopback there: `host: 0.0.0.0` in
|
|
89
|
+
`~/.pengepul/config.yaml` on the relay, or forward the port over SSH). The key
|
|
90
|
+
is the relay's own key — `pengepul config api-key` prints it.
|
|
91
|
+
|
|
92
|
+
Optional overrides, for CI or a one-off shell. Each is a fallback: the
|
|
93
|
+
credential wins when it carries a value.
|
|
94
|
+
|
|
63
95
|
| Setting | Env var | Default |
|
|
64
96
|
|---|---|---|
|
|
65
|
-
| Relay base URL | `PENGEPUL_BASE_URL` | `http://127.0.0.1:8317` |
|
|
66
|
-
| API key | `PENGEPUL_API_KEY` |
|
|
97
|
+
| Relay base URL | `PENGEPUL_BASE_URL` | `http://127.0.0.1:8317`, or the relay's own `config.yaml` |
|
|
98
|
+
| API key | `PENGEPUL_API_KEY` | `api-keys[0]` in `~/.pengepul/config.yaml` |
|
|
67
99
|
| Config path | `PENGEPUL_CONFIG` | `~/.pengepul/config.yaml` |
|
|
68
|
-
|
|
|
100
|
+
| Legacy cache path | `PENGEPUL_MODELS_CACHE` | `<agent-dir>/pengepul-models.json` |
|
|
69
101
|
| Discovery timeout | `PENGEPUL_MODELS_TIMEOUT_MS` | `10000` |
|
|
70
102
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
103
|
+
`PENGEPUL_MODELS_CACHE` names the pre-0.3 cache file. It is read once, when pi's
|
|
104
|
+
model store has no pengepul catalog yet, and never written again.
|
|
105
|
+
|
|
106
|
+
Do not set `providers.pengepul.baseUrl` in `models.json`. pi applies that value
|
|
107
|
+
to every model of the provider, which collapses the two wires onto one URL —
|
|
108
|
+
Anthropic Messages traffic would be sent to `/v1` and Chat Completions traffic
|
|
109
|
+
to `/`.
|
|
74
110
|
|
|
75
111
|
## Notes
|
|
76
112
|
|
|
@@ -81,7 +117,9 @@ is set.
|
|
|
81
117
|
pengepul bills against — displayed costs are upstream list prices.
|
|
82
118
|
- The relay must be running and reachable for discovery to succeed. Without a
|
|
83
119
|
cached catalog on a first start, pengepul models stay unavailable until a
|
|
84
|
-
start with the relay up.
|
|
120
|
+
start with the relay up. A relay that answers with 401 leaves the last known
|
|
121
|
+
catalog registered and logs a warning; discovery does not take pi down with
|
|
122
|
+
it.
|
|
85
123
|
|
|
86
124
|
## Development
|
|
87
125
|
|
package/extensions/api-key.ts
CHANGED
|
@@ -1,53 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Read pengepul's own API key.
|
|
3
3
|
*
|
|
4
4
|
* pengepul authenticates every request with a static key from its config:
|
|
5
5
|
* `~/.pengepul/config.yaml`, under `api-keys:` (the first is generated on first
|
|
6
6
|
* run and is `sk-local-...`). Clients send it as `Authorization: Bearer <key>`
|
|
7
7
|
* or `x-api-key: <key>`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* This is the fallback for the machine that runs the relay itself: a client box
|
|
10
|
+
* configures the key in `auth.json`, which pi resolves and hands to the
|
|
11
|
+
* provider. Only extraction lives here, so the file read stays with the caller
|
|
12
|
+
* and this module stays io-free.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
const DEFAULT_CONFIG_PATH = "~/.pengepul/config.yaml"
|
|
14
|
-
|
|
15
|
-
export type ApiKeySource = "env" | "config" | "none"
|
|
16
|
-
|
|
17
|
-
export interface ApiKeyResolution {
|
|
18
|
-
/** The resolved key, or undefined when none could be found. */
|
|
19
|
-
key?: string
|
|
20
|
-
source: ApiKeySource
|
|
21
|
-
}
|
|
15
|
+
export const DEFAULT_CONFIG_PATH = "~/.pengepul/config.yaml"
|
|
22
16
|
|
|
23
17
|
export const API_KEY_ENV = "PENGEPUL_API_KEY"
|
|
24
18
|
export const CONFIG_PATH_ENV = "PENGEPUL_CONFIG"
|
|
25
19
|
|
|
26
|
-
/**
|
|
27
|
-
* Resolve the key from an env map and a config-file reader.
|
|
28
|
-
*
|
|
29
|
-
* @param env the environment (or a test substitution for it).
|
|
30
|
-
* @param readConfig reads a config file's text by path, or undefined when the
|
|
31
|
-
* path is unwritable/absent. Injected to keep this pure.
|
|
32
|
-
*/
|
|
33
|
-
export function resolveApiKey(
|
|
34
|
-
env: Record<string, string | undefined>,
|
|
35
|
-
readConfig: (path: string) => string | undefined,
|
|
36
|
-
): ApiKeyResolution {
|
|
37
|
-
const envKey = env[API_KEY_ENV]
|
|
38
|
-
if (envKey && envKey.trim() !== "") return { key: envKey, source: "env" }
|
|
39
|
-
|
|
40
|
-
const configPath = env[CONFIG_PATH_ENV] ?? DEFAULT_CONFIG_PATH
|
|
41
|
-
const configText = readConfig(configPath)
|
|
42
|
-
if (configText === undefined) return { source: "none" }
|
|
43
|
-
|
|
44
|
-
const keys = extractApiKeys(configText)
|
|
45
|
-
const first = keys[0]
|
|
46
|
-
if (first) return { key: first, source: "config" }
|
|
47
|
-
|
|
48
|
-
return { source: "none" }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
20
|
/**
|
|
52
21
|
* Extract `api-keys:` entries from pengepul's YAML config, without a YAML
|
|
53
22
|
* dependency. Handles both the inline-flow form and the block-sequence form:
|
package/extensions/config.ts
CHANGED
|
@@ -1,40 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Where the pengepul provider's fallback sources live.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `auth.json` is the configuration surface. These are the escape hatches kept
|
|
5
|
+
* for tests, CI, and a machine that happens to run the relay itself: env vars
|
|
6
|
+
* for the relay base and key, and pengepul's own config on disk.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import { join } from "node:path"
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
+
import { CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH } from "./api-key.ts"
|
|
12
|
+
|
|
13
|
+
export { CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH }
|
|
11
14
|
|
|
12
15
|
export const RELAY_BASE_ENV = "PENGEPUL_BASE_URL"
|
|
13
16
|
export const MODELS_CACHE_ENV = "PENGEPUL_MODELS_CACHE"
|
|
14
17
|
export const MODELS_TIMEOUT_MS_ENV = "PENGEPUL_MODELS_TIMEOUT_MS"
|
|
15
18
|
|
|
16
19
|
export interface PengepulSettings {
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
/** Where the
|
|
20
|
-
|
|
21
|
-
/** Discovery timeout in milliseconds. */
|
|
22
|
-
modelsTimeoutMs: number
|
|
20
|
+
/** Where pengepul's own config lives, when this machine runs the relay. */
|
|
21
|
+
configPath: string
|
|
22
|
+
/** Where the pre-0.3 catalog cache lives; read once to seed pi's store. */
|
|
23
|
+
legacyCachePath: string
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function resolveSettings(
|
|
26
27
|
env: Record<string, string | undefined>,
|
|
27
28
|
agentDir: string,
|
|
28
29
|
): PengepulSettings {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
env[MODELS_CACHE_ENV] ?? join(agentDir, "pengepul-models.json")
|
|
32
|
-
|
|
33
|
-
const parsedTimeout = rawTimeout ? Number(rawTimeout) : NaN
|
|
34
|
-
const modelsTimeoutMs =
|
|
35
|
-
Number.isFinite(parsedTimeout) && parsedTimeout > 0
|
|
36
|
-
? parsedTimeout
|
|
37
|
-
: 10_000
|
|
38
|
-
|
|
39
|
-
return { relayBase, modelsCachePath, modelsTimeoutMs }
|
|
30
|
+
return {
|
|
31
|
+
configPath: env[CONFIG_PATH_ENV] ?? DEFAULT_CONFIG_PATH,
|
|
32
|
+
legacyCachePath: env[MODELS_CACHE_ENV] ?? join(agentDir, "pengepul-models.json"),
|
|
33
|
+
}
|
|
40
34
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pengepul credential and relay-base resolution.
|
|
3
|
+
*
|
|
4
|
+
* The credential is the pengepul entry in pi's `auth.json`: an API key for the
|
|
5
|
+
* relay plus the relay base it applies to. pi core resolves the key itself but
|
|
6
|
+
* knows nothing about `baseUrl`, so the extension reads it here. Everything in
|
|
7
|
+
* this module is pure: an env map and config text in, a value out.
|
|
8
|
+
*
|
|
9
|
+
* Precedence, highest first: credential, then environment, then the relay's own
|
|
10
|
+
* config file (which only exists on the machine running the relay), then the
|
|
11
|
+
* loopback default pengepul binds to.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { normalizeRootBaseUrl } from "./dialect.ts"
|
|
15
|
+
import { DEFAULT_RELAY_BASE } from "./models.ts"
|
|
16
|
+
|
|
17
|
+
/** The fields this provider reads off a stored credential. */
|
|
18
|
+
export interface PengepulCredential {
|
|
19
|
+
/** Always `"api_key"` in pi's store; read as unknown so a hand-edited file cannot lie its way past a cast. */
|
|
20
|
+
type?: unknown
|
|
21
|
+
key?: unknown
|
|
22
|
+
baseUrl?: unknown
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function nonEmptyString(value: unknown): string | undefined {
|
|
26
|
+
if (typeof value !== "string") return undefined
|
|
27
|
+
const trimmed = value.trim()
|
|
28
|
+
return trimmed === "" ? undefined : trimmed
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The relay base stored on the credential, when the user set one. */
|
|
32
|
+
export function credentialRelayBase(credential: PengepulCredential | undefined): string | undefined {
|
|
33
|
+
return nonEmptyString(credential?.baseUrl)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The API key stored on the credential, when the user set one. */
|
|
37
|
+
export function credentialApiKey(credential: PengepulCredential | undefined): string | undefined {
|
|
38
|
+
return nonEmptyString(credential?.key)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Build a relay base from pengepul's own `config.yaml`. Only the machine running
|
|
43
|
+
* the relay has this file; clients reach a relay elsewhere and skip this source.
|
|
44
|
+
*
|
|
45
|
+
* pengepul writes `host: ''` when it binds loopback ("empty binds 127.0.0.1, not
|
|
46
|
+
* every interface"), so an empty host means loopback rather than "unset". A
|
|
47
|
+
* bind-all address is not a connect address either: `0.0.0.0`, `::`, and `*` mean
|
|
48
|
+
* "every interface", and a client reaches that listener on loopback.
|
|
49
|
+
*/
|
|
50
|
+
export function relayBaseFromConfigText(configText: string | undefined): string | undefined {
|
|
51
|
+
if (configText === undefined) return undefined
|
|
52
|
+
|
|
53
|
+
let host = "127.0.0.1"
|
|
54
|
+
let port: number | undefined
|
|
55
|
+
|
|
56
|
+
for (const line of configText.split(/\r?\n/)) {
|
|
57
|
+
const hostMatch = /^\s*host:\s*(.*)$/.exec(line)
|
|
58
|
+
if (hostMatch) {
|
|
59
|
+
const value = (hostMatch[1] ?? "").trim().replace(/^["']|["']$/g, "")
|
|
60
|
+
if (value !== "" && !isBindAll(value)) host = value
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const portMatch = /^\s*port:\s*(.*)$/.exec(line)
|
|
65
|
+
if (portMatch) {
|
|
66
|
+
const value = (portMatch[1] ?? "").trim().replace(/^["']|["']$/g, "")
|
|
67
|
+
const parsed = Number(value)
|
|
68
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65_535) port = parsed
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return port === undefined ? undefined : `http://${host}:${port}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Whether a configured host is a bind-all address rather than a destination. */
|
|
76
|
+
function isBindAll(host: string): boolean {
|
|
77
|
+
return host === "0.0.0.0" || host === "::" || host === "[::]" || host === "*"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface RelayBaseSources {
|
|
81
|
+
/** The relay base stored on the pengepul credential. */
|
|
82
|
+
credential?: string | undefined
|
|
83
|
+
/** The `PENGEPUL_BASE_URL` environment value. */
|
|
84
|
+
environment?: string | undefined
|
|
85
|
+
/** The relay base derived from pengepul's own config file. */
|
|
86
|
+
config?: string | undefined
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The relay root every wire appends to: highest-precedence non-blank source, or
|
|
91
|
+
* loopback. A trailing `/v1` is accepted and stripped, so a base copied from a
|
|
92
|
+
* client config cannot produce `/v1/v1` downstream.
|
|
93
|
+
*/
|
|
94
|
+
export function resolveRelayBase(sources: RelayBaseSources): string {
|
|
95
|
+
const chosen =
|
|
96
|
+
nonEmptyString(sources.credential) ??
|
|
97
|
+
nonEmptyString(sources.environment) ??
|
|
98
|
+
nonEmptyString(sources.config) ??
|
|
99
|
+
DEFAULT_RELAY_BASE
|
|
100
|
+
return normalizeRootBaseUrl(chosen)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ApiKeySources {
|
|
104
|
+
/** The key stored on the pengepul credential. */
|
|
105
|
+
credential?: string | undefined
|
|
106
|
+
/** The key resolved from `PENGEPUL_API_KEY` or pengepul's own config file. */
|
|
107
|
+
ambient?: string | undefined
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The key requests and catalog fetches use, or undefined when none is configured. */
|
|
111
|
+
export function resolveApiKey(sources: ApiKeySources): string | undefined {
|
|
112
|
+
return nonEmptyString(sources.credential) ?? nonEmptyString(sources.ambient)
|
|
113
|
+
}
|
package/extensions/index.ts
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @pwguler/pi-pengepul-provider entry point - the real edge adapter.
|
|
3
3
|
*
|
|
4
|
-
* Registers pengepul as a pi
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Registers pengepul as a pi provider. pengepul is a relay that pools your
|
|
5
|
+
* Claude/Codex subscriptions and speaks both native wires. The provider itself
|
|
6
|
+
* lives in `./provider.ts`: pi resolves auth through it and hands the credential
|
|
7
|
+
* back on every refresh, so `auth.json` carries the key and the relay base. The
|
|
8
|
+
* pure core is `./credential.ts`, `./dialect.ts`, and `./models.ts`; this file
|
|
9
|
+
* adapts them to the pi ExtensionAPI seam and reads the one file pi cannot.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import {
|
|
11
13
|
getAgentDir,
|
|
12
14
|
type ExtensionAPI,
|
|
13
|
-
type ProviderConfig,
|
|
14
15
|
} from "@earendil-works/pi-coding-agent"
|
|
15
16
|
import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from "@earendil-works/pi-ai/providers/all"
|
|
16
17
|
import { readFileSync } from "node:fs"
|
|
17
18
|
|
|
18
|
-
import { resolveApiKey } from "./api-key.ts"
|
|
19
19
|
import { resolveSettings } from "./config.ts"
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
catalogIdForms,
|
|
23
|
-
loadCachedPengepulModels,
|
|
24
|
-
loadPengepulModels,
|
|
25
|
-
toProviderModelConfigs,
|
|
26
|
-
type PengepulModel,
|
|
27
|
-
} from "./models.ts"
|
|
28
|
-
import { createPengepulRuntime } from "./runtime.ts"
|
|
20
|
+
import { catalogIdForms, type PengepulModel } from "./models.ts"
|
|
21
|
+
import { createPengepulProvider } from "./provider.ts"
|
|
29
22
|
|
|
30
23
|
function expandHome(path: string): string {
|
|
31
24
|
if (path === "~") return process.env.HOME ?? path
|
|
@@ -93,49 +86,24 @@ function createBuiltinLookup(): (id: string, dialect: string) => ReturnType<type
|
|
|
93
86
|
}
|
|
94
87
|
}
|
|
95
88
|
|
|
96
|
-
function createProviderConfigFactory(relayBase: string, apiKey: string | undefined) {
|
|
97
|
-
return (models: readonly PengepulModel[]): ProviderConfig => ({
|
|
98
|
-
name: "Pengepul",
|
|
99
|
-
baseUrl: relayBase,
|
|
100
|
-
apiKey: apiKey ?? "$PENGEPUL_API_KEY",
|
|
101
|
-
api: "anthropic-messages",
|
|
102
|
-
models: toProviderModelConfigs(models, relayBase),
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
106
89
|
/**
|
|
107
|
-
* Model discovery
|
|
108
|
-
*
|
|
109
|
-
*
|
|
90
|
+
* Model discovery belongs to pi: it calls `refreshModels` with the resolved
|
|
91
|
+
* credential, first against pi's cached catalog and then, when the network is
|
|
92
|
+
* allowed, against the relay. Registration is synchronous; nothing here waits
|
|
93
|
+
* on the relay, and the catalog survives a restart through pi's model store.
|
|
110
94
|
*/
|
|
111
|
-
export default
|
|
95
|
+
export default function (pi: ExtensionAPI) {
|
|
112
96
|
const settings = resolveSettings(process.env, getAgentDir())
|
|
113
|
-
const apiKey = resolveApiKey(process.env, readConfigText).key
|
|
114
97
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const runtime = createPengepulRuntime(pi, {
|
|
122
|
-
loadModels: (signal) =>
|
|
123
|
-
loadPengepulModels({
|
|
124
|
-
url: modelsUrl(settings.relayBase),
|
|
125
|
-
apiKey,
|
|
126
|
-
cachePath: settings.modelsCachePath,
|
|
127
|
-
relayBase: settings.relayBase,
|
|
128
|
-
timeoutMs: settings.modelsTimeoutMs,
|
|
129
|
-
lookupBuiltin,
|
|
130
|
-
signal,
|
|
131
|
-
}),
|
|
132
|
-
loadCachedModels: () => loadCachedPengepulModels(settings.modelsCachePath),
|
|
133
|
-
createProviderConfig: createProviderConfigFactory(settings.relayBase, apiKey),
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
pi.on("session_shutdown", () => {
|
|
137
|
-
runtime.dispose()
|
|
98
|
+
const provider = createPengepulProvider({
|
|
99
|
+
env: process.env,
|
|
100
|
+
configText: readConfigText(settings.configPath),
|
|
101
|
+
legacyCachePath: settings.legacyCachePath,
|
|
102
|
+
lookupBuiltin: createBuiltinLookup(),
|
|
138
103
|
})
|
|
139
104
|
|
|
140
|
-
|
|
105
|
+
pi.registerProvider(provider)
|
|
141
106
|
}
|
|
107
|
+
|
|
108
|
+
/** The pengepul catalog shape, re-exported for callers that build on the core. */
|
|
109
|
+
export type { PengepulModel }
|