@@ -43,7 +43,7 @@ export default function (pi: ExtensionAPI) {
|
||||
pi.registerProvider("my-provider", {
|
||||
name: "My Provider",
|
||||
baseUrl: "https://api.example.com",
|
||||
apiKey: "MY_API_KEY",
|
||||
apiKey: "$MY_API_KEY",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
@@ -83,7 +83,7 @@ pi.registerProvider("openai", {
|
||||
pi.registerProvider("google", {
|
||||
baseUrl: "https://ai-gateway.corp.com/google",
|
||||
headers: {
|
||||
"X-Corp-Auth": "CORP_AUTH_TOKEN" // env var or literal
|
||||
"X-Corp-Auth": "$CORP_AUTH_TOKEN" // env var or literal
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -112,7 +112,7 @@ export default async function (pi: ExtensionAPI) {
|
||||
|
||||
pi.registerProvider("local-openai", {
|
||||
baseUrl: "http://localhost:1234/v1",
|
||||
apiKey: "LOCAL_OPENAI_API_KEY",
|
||||
apiKey: "$LOCAL_OPENAI_API_KEY",
|
||||
api: "openai-completions",
|
||||
models: payload.data.map((model) => ({
|
||||
id: model.id,
|
||||
@@ -132,7 +132,7 @@ This registers the fetched models before startup finishes.
|
||||
```typescript
|
||||
pi.registerProvider("my-llm", {
|
||||
baseUrl: "https://api.my-llm.com/v1",
|
||||
apiKey: "MY_LLM_API_KEY", // env var name or literal value
|
||||
apiKey: "$MY_LLM_API_KEY", // env var reference
|
||||
api: "openai-completions", // which streaming API to use
|
||||
models: [
|
||||
{
|
||||
@@ -155,6 +155,8 @@ pi.registerProvider("my-llm", {
|
||||
|
||||
When `models` is provided, it **replaces** all existing models for that provider.
|
||||
|
||||
`apiKey` and custom header values use the same config value syntax as `models.json`: `!command` at the start executes a command for the whole value, `$ENV_VAR` and `${ENV_VAR}` interpolate environment variables, `$$` emits a literal `$`, and `$!` emits a literal `!`.
|
||||
|
||||
## Unregister Provider
|
||||
|
||||
Use `pi.unregisterProvider(name)` to remove a provider that was previously registered via `pi.registerProvider(name, ...)`:
|
||||
@@ -163,7 +165,7 @@ Use `pi.unregisterProvider(name)` to remove a provider that was previously regis
|
||||
// Register
|
||||
pi.registerProvider("my-llm", {
|
||||
baseUrl: "https://api.my-llm.com/v1",
|
||||
apiKey: "MY_LLM_API_KEY",
|
||||
apiKey: "$MY_LLM_API_KEY",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
@@ -243,7 +245,7 @@ If your provider expects `Authorization: Bearer <key>` but doesn't use a standar
|
||||
```typescript
|
||||
pi.registerProvider("custom-api", {
|
||||
baseUrl: "https://api.example.com",
|
||||
apiKey: "MY_API_KEY",
|
||||
apiKey: "$MY_API_KEY",
|
||||
authHeader: true, // adds Authorization: Bearer header
|
||||
api: "openai-completions",
|
||||
models: [...]
|
||||
@@ -592,7 +594,7 @@ Register your stream function:
|
||||
```typescript
|
||||
pi.registerProvider("my-provider", {
|
||||
baseUrl: "https://api.example.com",
|
||||
apiKey: "MY_API_KEY",
|
||||
apiKey: "$MY_API_KEY",
|
||||
api: "my-custom-api",
|
||||
models: [...],
|
||||
streamSimple: streamMyProvider
|
||||
@@ -629,7 +631,7 @@ interface ProviderConfig {
|
||||
/** API endpoint URL. Required when defining models. */
|
||||
baseUrl?: string;
|
||||
|
||||
/** API key or environment variable name. Required when defining models (unless oauth). */
|
||||
/** API key literal, env interpolation ($ENV_VAR or ${ENV_VAR}), or !command. Required when defining models (unless oauth). */
|
||||
apiKey?: string;
|
||||
|
||||
/** API type for streaming. Required at provider or model level when defining models. */
|
||||
@@ -642,7 +644,7 @@ interface ProviderConfig {
|
||||
options?: SimpleStreamOptions
|
||||
) => AssistantMessageEventStream;
|
||||
|
||||
/** Custom headers to include in requests. Values can be env var names. */
|
||||
/** Custom headers to include in requests. Values use the same resolution syntax as apiKey. */
|
||||
headers?: Record<string, string>;
|
||||
|
||||
/** If true, adds Authorization: Bearer header with the resolved API key. */
|
||||
|
||||
@@ -199,7 +199,7 @@ export default async function (pi: ExtensionAPI) {
|
||||
|
||||
pi.registerProvider("local-openai", {
|
||||
baseUrl: "http://localhost:1234/v1",
|
||||
apiKey: "LOCAL_OPENAI_API_KEY",
|
||||
apiKey: "$LOCAL_OPENAI_API_KEY",
|
||||
api: "openai-completions",
|
||||
models: payload.data.map((model) => ({
|
||||
id: model.id,
|
||||
@@ -1555,7 +1555,7 @@ If you need to discover models from a remote endpoint, prefer an async extension
|
||||
pi.registerProvider("my-proxy", {
|
||||
name: "My Proxy",
|
||||
baseUrl: "https://proxy.example.com",
|
||||
apiKey: "PROXY_API_KEY", // env var name or literal
|
||||
apiKey: "$PROXY_API_KEY", // env var reference
|
||||
api: "anthropic-messages",
|
||||
models: [
|
||||
{
|
||||
@@ -1602,7 +1602,7 @@ pi.registerProvider("corporate-ai", {
|
||||
**Config options:**
|
||||
- `name` - Display name for the provider in UI such as `/login`.
|
||||
- `baseUrl` - API endpoint URL. Required when defining models.
|
||||
- `apiKey` - API key or environment variable name. Required when defining models (unless `oauth` provided).
|
||||
- `apiKey` - API key literal, environment interpolation (`$ENV_VAR` or `${ENV_VAR}`), or leading `!command`. Required when defining models (unless `oauth` provided). `$$` escapes `$`, and `$!` escapes a literal `!` without triggering command execution.
|
||||
- `api` - API type: `"anthropic-messages"`, `"openai-completions"`, `"openai-responses"`, etc.
|
||||
- `headers` - Custom headers to include in requests.
|
||||
- `authHeader` - If true, adds `Authorization: Bearer` header automatically.
|
||||
|
||||
@@ -101,7 +101,7 @@ Use `google-generative-ai` with a `baseUrl` to add models from Google AI Studio,
|
||||
"my-google": {
|
||||
"baseUrl": "https://generativelanguage.googleapis.com/v1beta",
|
||||
"api": "google-generative-ai",
|
||||
"apiKey": "GEMINI_API_KEY",
|
||||
"apiKey": "$GEMINI_API_KEY",
|
||||
"models": [
|
||||
{
|
||||
"id": "gemma-4-31b-it",
|
||||
@@ -143,22 +143,31 @@ Set `api` at provider level (default for all models) or model level (override pe
|
||||
|
||||
### Value Resolution
|
||||
|
||||
The `apiKey` and `headers` fields support three formats:
|
||||
The `apiKey` and `headers` fields support command execution, environment interpolation, and literals:
|
||||
|
||||
- **Shell command:** `"!command"` executes and uses stdout
|
||||
- **Shell command:** `"!command"` at the start executes the whole value as a command and uses stdout
|
||||
```json
|
||||
"apiKey": "!security find-generic-password -ws 'anthropic'"
|
||||
"apiKey": "!op read 'op://vault/item/credential'"
|
||||
```
|
||||
- **Environment variable:** Uses the value of the named variable
|
||||
- **Environment interpolation:** `"$ENV_VAR"` or `"${ENV_VAR}"` uses the value of the named variable. Interpolation works inside larger literals.
|
||||
```json
|
||||
"apiKey": "MY_API_KEY"
|
||||
"apiKey": "$MY_API_KEY"
|
||||
"apiKey": "${KEY_PREFIX}_${KEY_SUFFIX}"
|
||||
```
|
||||
`$FOO_BAR` is the variable `FOO_BAR`; use `${FOO}_BAR` when `BAR` is literal text. Missing environment variables make the value unresolved.
|
||||
- **Escapes:** `"$$"` emits a literal `"$"`; `"$!"` emits a literal `"!"` without triggering command execution.
|
||||
```json
|
||||
"apiKey": "$$literal-dollar-prefix"
|
||||
"apiKey": "$!literal-bang-prefix"
|
||||
```
|
||||
- **Literal value:** Used directly
|
||||
```json
|
||||
"apiKey": "sk-..."
|
||||
```
|
||||
|
||||
Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup.
|
||||
|
||||
For `models.json`, shell commands are resolved at request time. pi intentionally does not apply built-in TTL, stale reuse, or recovery logic for arbitrary commands. Different commands need different caching and failure strategies, and pi cannot infer the right one.
|
||||
|
||||
If your command is slow, expensive, rate-limited, or should keep using a previous value on transient failures, wrap it in your own script or command that implements the caching or TTL behavior you want.
|
||||
@@ -172,10 +181,10 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
|
||||
"providers": {
|
||||
"custom-proxy": {
|
||||
"baseUrl": "https://proxy.example.com/v1",
|
||||
"apiKey": "MY_API_KEY",
|
||||
"apiKey": "$MY_API_KEY",
|
||||
"api": "anthropic-messages",
|
||||
"headers": {
|
||||
"x-portkey-api-key": "PORTKEY_API_KEY",
|
||||
"x-portkey-api-key": "$PORTKEY_API_KEY",
|
||||
"x-secret": "!op read 'op://vault/item/secret'"
|
||||
},
|
||||
"models": [...]
|
||||
@@ -268,7 +277,7 @@ To merge custom models into a built-in provider, include the `models` array:
|
||||
"providers": {
|
||||
"anthropic": {
|
||||
"baseUrl": "https://my-proxy.example.com/v1",
|
||||
"apiKey": "ANTHROPIC_API_KEY",
|
||||
"apiKey": "$ANTHROPIC_API_KEY",
|
||||
"api": "anthropic-messages",
|
||||
"models": [...]
|
||||
}
|
||||
@@ -327,7 +336,7 @@ Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plu
|
||||
"anthropic-proxy": {
|
||||
"baseUrl": "https://proxy.example.com",
|
||||
"api": "anthropic-messages",
|
||||
"apiKey": "ANTHROPIC_PROXY_KEY",
|
||||
"apiKey": "$ANTHROPIC_PROXY_KEY",
|
||||
"compat": {
|
||||
"supportsEagerToolInputStreaming": false,
|
||||
"supportsLongCacheRetention": true,
|
||||
@@ -405,7 +414,7 @@ Example:
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"baseUrl": "https://openrouter.ai/api/v1",
|
||||
"apiKey": "OPENROUTER_API_KEY",
|
||||
"apiKey": "$OPENROUTER_API_KEY",
|
||||
"api": "openai-completions",
|
||||
"models": [
|
||||
{
|
||||
@@ -455,7 +464,7 @@ Vercel AI Gateway example:
|
||||
"providers": {
|
||||
"vercel-ai-gateway": {
|
||||
"baseUrl": "https://ai-gateway.vercel.sh/v1",
|
||||
"apiKey": "AI_GATEWAY_API_KEY",
|
||||
"apiKey": "$AI_GATEWAY_API_KEY",
|
||||
"api": "openai-completions",
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -101,23 +101,31 @@ The file is created with `0600` permissions (user read/write only). Auth file cr
|
||||
|
||||
### Key Resolution
|
||||
|
||||
The `key` field supports three formats:
|
||||
The `key` field supports command execution, environment interpolation, and literals:
|
||||
|
||||
- **Shell command:** `"!command"` executes and uses stdout (cached for process lifetime)
|
||||
- **Shell command:** `"!command"` at the start executes the whole value as a command and uses stdout (cached for process lifetime)
|
||||
```json
|
||||
{ "type": "api_key", "key": "!security find-generic-password -ws 'anthropic'" }
|
||||
{ "type": "api_key", "key": "!op read 'op://vault/item/credential'" }
|
||||
```
|
||||
- **Environment variable:** Uses the value of the named variable
|
||||
- **Environment interpolation:** `"$ENV_VAR"` or `"${ENV_VAR}"` uses the value of the named variable. Interpolation works inside larger literals.
|
||||
```json
|
||||
{ "type": "api_key", "key": "MY_ANTHROPIC_KEY" }
|
||||
{ "type": "api_key", "key": "$MY_ANTHROPIC_KEY" }
|
||||
{ "type": "api_key", "key": "${KEY_PREFIX}_${KEY_SUFFIX}" }
|
||||
```
|
||||
`$FOO_BAR` is the variable `FOO_BAR`; use `${FOO}_BAR` when `BAR` is literal text. Missing environment variables make the value unresolved.
|
||||
- **Escapes:** `"$$"` emits a literal `"$"`; `"$!"` emits a literal `"!"` without triggering command execution.
|
||||
```json
|
||||
{ "type": "api_key", "key": "$$literal-dollar-prefix" }
|
||||
{ "type": "api_key", "key": "$!literal-bang-prefix" }
|
||||
```
|
||||
- **Literal value:** Used directly
|
||||
```json
|
||||
{ "type": "api_key", "key": "sk-ant-..." }
|
||||
{ "type": "api_key", "key": "public" }
|
||||
```
|
||||
|
||||
OAuth credentials are also stored here after `/login` and managed automatically.
|
||||
Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup. OAuth credentials are also stored here after `/login` and managed automatically.
|
||||
|
||||
## Cloud Providers
|
||||
|
||||
|
||||
Reference in New Issue
Block a user