@inkdropapp/ai 0.1.2 → 0.1.4
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 +45 -0
- package/package.json +15 -5
- package/src/index.ts +9 -3
- package/src/provider-constants.ts +57 -0
- package/src/providers/anthropic.ts +10 -9
- package/src/providers/openai-compatible.ts +1 -20
- package/types/index.d.ts +3 -2
- package/types/provider-constants.d.ts +48 -0
- package/types/provider.d.ts +4 -1
- package/types/providers/openai-compatible.d.ts +0 -13
- package/types/registry.d.ts +16 -11
- package/types/settings.d.ts +12 -3
package/README.md
CHANGED
|
@@ -370,6 +370,51 @@ const settings: AISettings = {
|
|
|
370
370
|
`openrouter` looks up `OPENROUTER_API_KEY`; `ollama_local` looks up
|
|
371
371
|
`OLLAMA_LOCAL_API_KEY`. Each gets its own keyring entry under its `baseURL`.
|
|
372
372
|
|
|
373
|
+
### Adding an OpenAI-compatible provider at runtime
|
|
374
|
+
|
|
375
|
+
Two-step flow: persist the new entry into `AISettings` and rebuild the
|
|
376
|
+
registry, then set the API key on the resulting provider instance.
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
// 1. Add the entry to settings and persist (host owns the config layer).
|
|
380
|
+
const next: AISettings = {
|
|
381
|
+
...settings,
|
|
382
|
+
providers: {
|
|
383
|
+
...settings.providers,
|
|
384
|
+
openaiCompatible: [
|
|
385
|
+
...(settings.providers.openaiCompatible ?? []),
|
|
386
|
+
{
|
|
387
|
+
id: 'groq',
|
|
388
|
+
baseURL: 'https://api.groq.com/openai/v1',
|
|
389
|
+
models: [{ id: 'llama-3.3-70b-versatile' }]
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
writeConfigFile(next) // your persistence layer
|
|
395
|
+
registry.updateSettings(next)
|
|
396
|
+
|
|
397
|
+
// 2. The provider now exists in the registry — set its API key.
|
|
398
|
+
const provider = registry.getProvider('groq')!
|
|
399
|
+
await provider.setApiKey('gsk-...')
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
`updateSettings` rebuilds the provider list but preserves the shared
|
|
403
|
+
`KeyStore`, so any keys already in the keyring stay resolved.
|
|
404
|
+
|
|
405
|
+
If your UX needs to **validate the key before persisting the entry**, you can
|
|
406
|
+
write directly to the `KeyStore` first — keys are stored against the
|
|
407
|
+
normalised `baseURL`, not the provider id, so this works without the entry
|
|
408
|
+
being registered yet:
|
|
409
|
+
|
|
410
|
+
```ts
|
|
411
|
+
await registry.keyStore.setKey('https://api.groq.com/openai/v1', 'gsk-...')
|
|
412
|
+
// later, once validated: persist the AISettings entry and updateSettings.
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Once you add the entry and rebuild, `provider.isAuthenticated()` returns
|
|
416
|
+
`true` because the key already lives under that `baseURL`.
|
|
417
|
+
|
|
373
418
|
### Mixing slots across providers
|
|
374
419
|
|
|
375
420
|
```ts
|
package/package.json
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkdropapp/ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "AI integration common module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./types/index.d.ts",
|
|
11
|
+
"default": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"./constants": {
|
|
14
|
+
"types": "./types/provider-constants.d.ts",
|
|
15
|
+
"default": "./src/provider-constants.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
8
18
|
"scripts": {
|
|
9
19
|
"lint": "eslint src __tests__",
|
|
10
20
|
"test": "node --experimental-vm-modules --no-warnings=ExperimentalWarning node_modules/jest/bin/jest.js --config jest.config.cjs --runInBand",
|
|
11
|
-
"
|
|
21
|
+
"build": "tsc --declaration --emitDeclarationOnly",
|
|
12
22
|
"format": "prettier --write .",
|
|
13
|
-
"prepublishOnly": "npm-run-all lint test
|
|
23
|
+
"prepublishOnly": "npm-run-all lint test build"
|
|
14
24
|
},
|
|
15
25
|
"keywords": [
|
|
16
26
|
"ai",
|
|
@@ -25,7 +35,8 @@
|
|
|
25
35
|
"@ai-sdk/provider": "4.0.0-beta.14",
|
|
26
36
|
"@inkdropapp/ai-catalog": "^0.1.0",
|
|
27
37
|
"@napi-rs/keyring": "^1.2.0",
|
|
28
|
-
"ai": "7.0.0-beta.116"
|
|
38
|
+
"ai": "7.0.0-beta.116",
|
|
39
|
+
"npm-run-all2": "^8.0.4"
|
|
29
40
|
},
|
|
30
41
|
"devDependencies": {
|
|
31
42
|
"@types/jest": "^30.0.0",
|
|
@@ -33,7 +44,6 @@
|
|
|
33
44
|
"eslint": "^10.3.0",
|
|
34
45
|
"eslint-config-prettier": "^10.1.8",
|
|
35
46
|
"jest": "^30.4.2",
|
|
36
|
-
"npm-run-all": "^4.1.5",
|
|
37
47
|
"prettier": "^3.8.3",
|
|
38
48
|
"ts-jest": "^29.4.9",
|
|
39
49
|
"typescript": "^6.0.3",
|
package/src/index.ts
CHANGED
|
@@ -36,11 +36,17 @@ export {
|
|
|
36
36
|
} from './errors.js'
|
|
37
37
|
export { KeyStore, type KeyStoreOptions } from './key-store.js'
|
|
38
38
|
export { normalizeBaseURL } from './url.js'
|
|
39
|
-
export { AnthropicProvider } from './providers/anthropic.js'
|
|
40
39
|
export {
|
|
41
|
-
|
|
40
|
+
ANTHROPIC_PROVIDER_ID,
|
|
41
|
+
ANTHROPIC_PROVIDER_NAME,
|
|
42
|
+
ANTHROPIC_ENV_VAR,
|
|
43
|
+
ANTHROPIC_DEFAULT_BASE_URL,
|
|
44
|
+
OPENAI_COMPATIBLE_PROVIDER_ID,
|
|
45
|
+
OPENAI_COMPATIBLE_PROVIDER_NAME,
|
|
42
46
|
deriveEnvVarName
|
|
43
|
-
} from './
|
|
47
|
+
} from './provider-constants.js'
|
|
48
|
+
export { AnthropicProvider } from './providers/anthropic.js'
|
|
49
|
+
export { OpenAICompatibleProvider } from './providers/openai-compatible.js'
|
|
44
50
|
export {
|
|
45
51
|
AIRegistry,
|
|
46
52
|
type AIRegistryOptions,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-data provider metadata for settings UIs.
|
|
3
|
+
*
|
|
4
|
+
* This module has no class or native-module imports, so it's safe to load in
|
|
5
|
+
* an Electron renderer process — unlike the main package entrypoint, which
|
|
6
|
+
* transitively pulls in `@napi-rs/keyring`.
|
|
7
|
+
*
|
|
8
|
+
* Consume via the `@inkdropapp/ai/constants` subpath:
|
|
9
|
+
*
|
|
10
|
+
* import {
|
|
11
|
+
* ANTHROPIC_PROVIDER_ID,
|
|
12
|
+
* ANTHROPIC_PROVIDER_NAME,
|
|
13
|
+
* ANTHROPIC_ENV_VAR,
|
|
14
|
+
* ANTHROPIC_DEFAULT_BASE_URL,
|
|
15
|
+
* OPENAI_COMPATIBLE_PROVIDER_ID,
|
|
16
|
+
* OPENAI_COMPATIBLE_PROVIDER_NAME,
|
|
17
|
+
* deriveEnvVarName
|
|
18
|
+
* } from '@inkdropapp/ai/constants'
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export const ANTHROPIC_PROVIDER_ID = 'anthropic'
|
|
22
|
+
export const ANTHROPIC_PROVIDER_NAME = 'Anthropic'
|
|
23
|
+
export const ANTHROPIC_ENV_VAR = 'ANTHROPIC_API_KEY'
|
|
24
|
+
export const ANTHROPIC_DEFAULT_BASE_URL = 'https://api.anthropic.com'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The `AISettings.providers` key for OpenAI-compatible entries. Use this to
|
|
28
|
+
* tag a settings-UI row as "an OpenAI-compatible provider" (vs. the built-in
|
|
29
|
+
* Anthropic row).
|
|
30
|
+
*
|
|
31
|
+
* Runtime provider ids for OpenAI-compatible entries are user-chosen
|
|
32
|
+
* (`AISettings.providers.openaiCompatible[].id`); this constant is the
|
|
33
|
+
* *type/kind*, not an instance id.
|
|
34
|
+
*/
|
|
35
|
+
export const OPENAI_COMPATIBLE_PROVIDER_ID = 'openaiCompatible'
|
|
36
|
+
export const OPENAI_COMPATIBLE_PROVIDER_NAME = 'OpenAI-Compatible'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Derives the env-var name for an OpenAI-compatible provider entry from its
|
|
40
|
+
* user-chosen `id`.
|
|
41
|
+
*
|
|
42
|
+
* Rules: replace any non-alphanumeric character with `_`, collapse runs of `_`,
|
|
43
|
+
* uppercase, append `_API_KEY`.
|
|
44
|
+
*
|
|
45
|
+
* Examples:
|
|
46
|
+
* `openrouter` → `OPENROUTER_API_KEY`
|
|
47
|
+
* `together_ai` → `TOGETHER_AI_API_KEY`
|
|
48
|
+
* `ollama-local` → `OLLAMA_LOCAL_API_KEY`
|
|
49
|
+
* `my.proxy` → `MY_PROXY_API_KEY`
|
|
50
|
+
*/
|
|
51
|
+
export const deriveEnvVarName = (id: string): string => {
|
|
52
|
+
const sanitized = id
|
|
53
|
+
.replace(/[^a-zA-Z0-9]+/g, '_')
|
|
54
|
+
.replace(/^_+|_+$/g, '')
|
|
55
|
+
.toUpperCase()
|
|
56
|
+
return `${sanitized}_API_KEY`
|
|
57
|
+
}
|
|
@@ -18,6 +18,12 @@ import { NoApiKey } from '../errors.js'
|
|
|
18
18
|
import type { KeyStore } from '../key-store.js'
|
|
19
19
|
import { mapAiSdkError } from '../internal/map-ai-sdk-error.js'
|
|
20
20
|
import { mapAiSdkStream } from '../internal/map-ai-sdk-stream.js'
|
|
21
|
+
import {
|
|
22
|
+
ANTHROPIC_DEFAULT_BASE_URL,
|
|
23
|
+
ANTHROPIC_ENV_VAR,
|
|
24
|
+
ANTHROPIC_PROVIDER_ID,
|
|
25
|
+
ANTHROPIC_PROVIDER_NAME
|
|
26
|
+
} from '../provider-constants.js'
|
|
21
27
|
import type { AIModel, AIProvider, CompletionRequest } from '../provider.js'
|
|
22
28
|
import type {
|
|
23
29
|
AnthropicModelConfig,
|
|
@@ -25,11 +31,6 @@ import type {
|
|
|
25
31
|
} from '../settings.js'
|
|
26
32
|
import type { StreamEvent } from '../stream-events.js'
|
|
27
33
|
|
|
28
|
-
const PROVIDER_ID = 'anthropic'
|
|
29
|
-
const PROVIDER_NAME = 'Anthropic'
|
|
30
|
-
const ENV_VAR = 'ANTHROPIC_API_KEY'
|
|
31
|
-
const DEFAULT_BASE_URL = 'https://api.anthropic.com'
|
|
32
|
-
|
|
33
34
|
/**
|
|
34
35
|
* The Anthropic (Claude) provider.
|
|
35
36
|
*
|
|
@@ -45,9 +46,9 @@ const DEFAULT_BASE_URL = 'https://api.anthropic.com'
|
|
|
45
46
|
* `clearApiKey` is called or the resolved API key changes.
|
|
46
47
|
*/
|
|
47
48
|
export class AnthropicProvider implements AIProvider {
|
|
48
|
-
readonly id =
|
|
49
|
-
readonly name =
|
|
50
|
-
readonly envVarName =
|
|
49
|
+
readonly id = ANTHROPIC_PROVIDER_ID
|
|
50
|
+
readonly name = ANTHROPIC_PROVIDER_NAME
|
|
51
|
+
readonly envVarName = ANTHROPIC_ENV_VAR
|
|
51
52
|
readonly baseURL: string
|
|
52
53
|
|
|
53
54
|
private readonly keyStore: KeyStore
|
|
@@ -64,7 +65,7 @@ export class AnthropicProvider implements AIProvider {
|
|
|
64
65
|
catalog?: AnthropicCatalog
|
|
65
66
|
) {
|
|
66
67
|
this.keyStore = keyStore
|
|
67
|
-
this.baseURL = config.baseURL ??
|
|
68
|
+
this.baseURL = config.baseURL ?? ANTHROPIC_DEFAULT_BASE_URL
|
|
68
69
|
|
|
69
70
|
const baseModels = catalog?.models ?? ANTHROPIC_MODELS
|
|
70
71
|
this.defaultId = catalog?.defaultModelId ?? ANTHROPIC_DEFAULT_MODEL_ID
|
|
@@ -11,6 +11,7 @@ import { NoApiKey } from '../errors.js'
|
|
|
11
11
|
import type { KeyStore } from '../key-store.js'
|
|
12
12
|
import { mapAiSdkError } from '../internal/map-ai-sdk-error.js'
|
|
13
13
|
import { mapAiSdkStream } from '../internal/map-ai-sdk-stream.js'
|
|
14
|
+
import { deriveEnvVarName } from '../provider-constants.js'
|
|
14
15
|
import type { AIModel, AIProvider, CompletionRequest } from '../provider.js'
|
|
15
16
|
import type {
|
|
16
17
|
OpenAICompatibleModelConfig,
|
|
@@ -26,26 +27,6 @@ const DEFAULT_CAPABILITIES: ModelCapabilities = {
|
|
|
26
27
|
maxTokens: 32_000
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
/**
|
|
30
|
-
* Derives the env-var name for a user-named provider entry.
|
|
31
|
-
*
|
|
32
|
-
* Rules: replace any non-alphanumeric character with `_`, collapse runs of `_`,
|
|
33
|
-
* uppercase, append `_API_KEY`.
|
|
34
|
-
*
|
|
35
|
-
* Examples:
|
|
36
|
-
* `openrouter` → `OPENROUTER_API_KEY`
|
|
37
|
-
* `together_ai` → `TOGETHER_AI_API_KEY`
|
|
38
|
-
* `ollama-local` → `OLLAMA_LOCAL_API_KEY`
|
|
39
|
-
* `my.proxy` → `MY_PROXY_API_KEY`
|
|
40
|
-
*/
|
|
41
|
-
export const deriveEnvVarName = (id: string): string => {
|
|
42
|
-
const sanitized = id
|
|
43
|
-
.replace(/[^a-zA-Z0-9]+/g, '_')
|
|
44
|
-
.replace(/^_+|_+$/g, '')
|
|
45
|
-
.toUpperCase()
|
|
46
|
-
return `${sanitized}_API_KEY`
|
|
47
|
-
}
|
|
48
|
-
|
|
49
30
|
const mergeCapabilities = (
|
|
50
31
|
override: Partial<ModelCapabilities> | undefined
|
|
51
32
|
): ModelCapabilities => ({
|
package/types/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { ANTHROPIC_MODELS, ANTHROPIC_DEFAULT_MODEL_ID, ANTHROPIC_DEFAULT_FAST_MO
|
|
|
5
5
|
export { AIError, NoApiKey, AuthenticationError, RateLimitExceeded, ServerOverloaded, PromptTooLarge, UpstreamError, isAIError, type AIErrorKind } from './errors.js';
|
|
6
6
|
export { KeyStore, type KeyStoreOptions } from './key-store.js';
|
|
7
7
|
export { normalizeBaseURL } from './url.js';
|
|
8
|
+
export { ANTHROPIC_PROVIDER_ID, ANTHROPIC_PROVIDER_NAME, ANTHROPIC_ENV_VAR, ANTHROPIC_DEFAULT_BASE_URL, OPENAI_COMPATIBLE_PROVIDER_ID, OPENAI_COMPATIBLE_PROVIDER_NAME, deriveEnvVarName } from './provider-constants.js';
|
|
8
9
|
export { AnthropicProvider } from './providers/anthropic.js';
|
|
9
|
-
export { OpenAICompatibleProvider
|
|
10
|
-
export {
|
|
10
|
+
export { OpenAICompatibleProvider } from './providers/openai-compatible.js';
|
|
11
|
+
export { AIRegistry, type AIRegistryOptions, type ResolvedSlot } from './registry.js';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-data provider metadata for settings UIs.
|
|
3
|
+
*
|
|
4
|
+
* This module has no class or native-module imports, so it's safe to load in
|
|
5
|
+
* an Electron renderer process — unlike the main package entrypoint, which
|
|
6
|
+
* transitively pulls in `@napi-rs/keyring`.
|
|
7
|
+
*
|
|
8
|
+
* Consume via the `@inkdropapp/ai/constants` subpath:
|
|
9
|
+
*
|
|
10
|
+
* import {
|
|
11
|
+
* ANTHROPIC_PROVIDER_ID,
|
|
12
|
+
* ANTHROPIC_PROVIDER_NAME,
|
|
13
|
+
* ANTHROPIC_ENV_VAR,
|
|
14
|
+
* ANTHROPIC_DEFAULT_BASE_URL,
|
|
15
|
+
* OPENAI_COMPATIBLE_PROVIDER_ID,
|
|
16
|
+
* OPENAI_COMPATIBLE_PROVIDER_NAME,
|
|
17
|
+
* deriveEnvVarName
|
|
18
|
+
* } from '@inkdropapp/ai/constants'
|
|
19
|
+
*/
|
|
20
|
+
export declare const ANTHROPIC_PROVIDER_ID = "anthropic";
|
|
21
|
+
export declare const ANTHROPIC_PROVIDER_NAME = "Anthropic";
|
|
22
|
+
export declare const ANTHROPIC_ENV_VAR = "ANTHROPIC_API_KEY";
|
|
23
|
+
export declare const ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
24
|
+
/**
|
|
25
|
+
* The `AISettings.providers` key for OpenAI-compatible entries. Use this to
|
|
26
|
+
* tag a settings-UI row as "an OpenAI-compatible provider" (vs. the built-in
|
|
27
|
+
* Anthropic row).
|
|
28
|
+
*
|
|
29
|
+
* Runtime provider ids for OpenAI-compatible entries are user-chosen
|
|
30
|
+
* (`AISettings.providers.openaiCompatible[].id`); this constant is the
|
|
31
|
+
* *type/kind*, not an instance id.
|
|
32
|
+
*/
|
|
33
|
+
export declare const OPENAI_COMPATIBLE_PROVIDER_ID = "openaiCompatible";
|
|
34
|
+
export declare const OPENAI_COMPATIBLE_PROVIDER_NAME = "OpenAI-Compatible";
|
|
35
|
+
/**
|
|
36
|
+
* Derives the env-var name for an OpenAI-compatible provider entry from its
|
|
37
|
+
* user-chosen `id`.
|
|
38
|
+
*
|
|
39
|
+
* Rules: replace any non-alphanumeric character with `_`, collapse runs of `_`,
|
|
40
|
+
* uppercase, append `_API_KEY`.
|
|
41
|
+
*
|
|
42
|
+
* Examples:
|
|
43
|
+
* `openrouter` → `OPENROUTER_API_KEY`
|
|
44
|
+
* `together_ai` → `TOGETHER_AI_API_KEY`
|
|
45
|
+
* `ollama-local` → `OLLAMA_LOCAL_API_KEY`
|
|
46
|
+
* `my.proxy` → `MY_PROXY_API_KEY`
|
|
47
|
+
*/
|
|
48
|
+
export declare const deriveEnvVarName: (id: string) => string;
|
package/types/provider.d.ts
CHANGED
|
@@ -45,7 +45,10 @@ export interface AIModel {
|
|
|
45
45
|
* OpenAI-compatible providers (OpenRouter, Together, Ollama, …) are modelled.
|
|
46
46
|
*/
|
|
47
47
|
export interface AIProvider {
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Stable provider id; appears in both `AISettings.providerId` and
|
|
50
|
+
* `AISettings.slots[*].providerId`.
|
|
51
|
+
*/
|
|
49
52
|
readonly id: string;
|
|
50
53
|
/** Human-readable label. */
|
|
51
54
|
readonly name: string;
|
|
@@ -2,19 +2,6 @@ import { type OpenAICompatibleProvider as SdkOpenAICompatibleProvider } from '@a
|
|
|
2
2
|
import type { KeyStore } from '../key-store.js';
|
|
3
3
|
import type { AIModel, AIProvider } from '../provider.js';
|
|
4
4
|
import type { OpenAICompatibleProviderConfig } from '../settings.js';
|
|
5
|
-
/**
|
|
6
|
-
* Derives the env-var name for a user-named provider entry.
|
|
7
|
-
*
|
|
8
|
-
* Rules: replace any non-alphanumeric character with `_`, collapse runs of `_`,
|
|
9
|
-
* uppercase, append `_API_KEY`.
|
|
10
|
-
*
|
|
11
|
-
* Examples:
|
|
12
|
-
* `openrouter` → `OPENROUTER_API_KEY`
|
|
13
|
-
* `together_ai` → `TOGETHER_AI_API_KEY`
|
|
14
|
-
* `ollama-local` → `OLLAMA_LOCAL_API_KEY`
|
|
15
|
-
* `my.proxy` → `MY_PROXY_API_KEY`
|
|
16
|
-
*/
|
|
17
|
-
export declare const deriveEnvVarName: (id: string) => string;
|
|
18
5
|
/**
|
|
19
6
|
* Provider for any OpenAI-compatible chat-completions endpoint
|
|
20
7
|
* (OpenRouter, Together, Fireworks, Groq, vLLM, Ollama via `/v1`, LiteLLM, …).
|
package/types/registry.d.ts
CHANGED
|
@@ -8,13 +8,13 @@ export type ResolvedSlot = {
|
|
|
8
8
|
provider: AIProvider;
|
|
9
9
|
model: AIModel;
|
|
10
10
|
};
|
|
11
|
-
export type
|
|
11
|
+
export type AIRegistryOptions = {
|
|
12
12
|
settings: AISettings;
|
|
13
13
|
/**
|
|
14
14
|
* Optional server-distributed catalogue of supported models per provider.
|
|
15
15
|
* When omitted, each provider uses its compiled-in defaults (e.g. `ANTHROPIC_MODELS`).
|
|
16
16
|
* The host typically fetches this from an API and either passes it at
|
|
17
|
-
* construction time or swaps it in later via {@link
|
|
17
|
+
* construction time or swaps it in later via {@link AIRegistry.updateCatalog}.
|
|
18
18
|
*/
|
|
19
19
|
catalog?: AIModelCatalog;
|
|
20
20
|
/** Pass an existing `KeyStore` to share its in-memory cache across registries. */
|
|
@@ -26,25 +26,25 @@ export type RegistryOptions = {
|
|
|
26
26
|
* Owns one instance of every configured provider, resolves task slots with
|
|
27
27
|
* fallback, and is the only place feature code calls to start a completion.
|
|
28
28
|
*
|
|
29
|
-
* The `
|
|
30
|
-
* settings on its own. Call {@link
|
|
29
|
+
* The `AIRegistry` is stateless beyond its provider list — it doesn't observe
|
|
30
|
+
* settings on its own. Call {@link AIRegistry.updateSettings} when settings
|
|
31
31
|
* change to rebuild providers.
|
|
32
32
|
*
|
|
33
33
|
* @example
|
|
34
34
|
* ```ts
|
|
35
|
-
* const registry = new
|
|
35
|
+
* const registry = new AIRegistry({ settings })
|
|
36
36
|
* const events = await registry.streamCompletion('default', { messages })
|
|
37
37
|
* for await (const event of events) {
|
|
38
38
|
* if (event.kind === 'text-delta') process.stdout.write(event.delta)
|
|
39
39
|
* }
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
export declare class
|
|
42
|
+
export declare class AIRegistry {
|
|
43
43
|
readonly keyStore: KeyStore;
|
|
44
44
|
private providers;
|
|
45
45
|
private settings;
|
|
46
46
|
private catalog;
|
|
47
|
-
constructor(options:
|
|
47
|
+
constructor(options: AIRegistryOptions);
|
|
48
48
|
/** All configured providers, sorted alphabetically by id. */
|
|
49
49
|
listProviders(): AIProvider[];
|
|
50
50
|
getProvider(id: string): AIProvider | undefined;
|
|
@@ -59,12 +59,17 @@ export declare class Registry {
|
|
|
59
59
|
* Order:
|
|
60
60
|
* 1. Explicit binding `settings.slots[slot]`, if both provider and model resolve.
|
|
61
61
|
* 2. For `'fast'` only: fall back to `settings.slots.default`.
|
|
62
|
-
* 3.
|
|
62
|
+
* 3. Preferred provider `settings.providerId`, if that provider is configured,
|
|
63
|
+
* taking its `defaultModel()` (or `defaultFastModel()` for the fast slot).
|
|
64
|
+
* Authentication is not gated here — the user explicitly picked this
|
|
65
|
+
* provider; auth errors surface at stream time rather than silently
|
|
66
|
+
* swapping providers.
|
|
67
|
+
* 4. First authenticated provider in alphabetical id order, taking that
|
|
63
68
|
* provider's `defaultModel()` (or `defaultFastModel()` for the fast slot).
|
|
64
69
|
* Mirrors Zed's `available_fallback_model` without the Zed-Cloud preference.
|
|
65
|
-
*
|
|
70
|
+
* 5. `undefined` if no provider can satisfy the slot.
|
|
66
71
|
*
|
|
67
|
-
* Async because the
|
|
72
|
+
* Async because the fourth step calls `provider.isAuthenticated()`, which may
|
|
68
73
|
* touch the keyring.
|
|
69
74
|
*/
|
|
70
75
|
resolveSlot(slot: SlotName): Promise<ResolvedSlot | undefined>;
|
|
@@ -72,7 +77,7 @@ export declare class Registry {
|
|
|
72
77
|
/**
|
|
73
78
|
* Streams a completion for a task slot.
|
|
74
79
|
*
|
|
75
|
-
* Resolves the slot via {@link
|
|
80
|
+
* Resolves the slot via {@link AIRegistry.resolveSlot}; throws {@link NoApiKey}
|
|
76
81
|
* if no provider can satisfy it (no slot binding, and no authenticated
|
|
77
82
|
* provider available for the implicit fallback). Otherwise returns the
|
|
78
83
|
* model's stream — note that *upstream* errors (auth, rate limit, etc.)
|
package/types/settings.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type AnthropicProviderConfig = CommonProviderConfig & {
|
|
|
77
77
|
};
|
|
78
78
|
/**
|
|
79
79
|
* Top-level AI configuration. The host (Inkdrop main process) constructs this
|
|
80
|
-
* from whichever persistence layer it uses and passes it to `
|
|
80
|
+
* from whichever persistence layer it uses and passes it to `AIRegistry`.
|
|
81
81
|
*
|
|
82
82
|
* The library never reads or writes settings to disk itself.
|
|
83
83
|
*/
|
|
@@ -87,8 +87,17 @@ export type AISettings = {
|
|
|
87
87
|
openaiCompatible?: OpenAICompatibleProviderConfig[];
|
|
88
88
|
};
|
|
89
89
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
90
|
+
* Preferred provider for every task slot. When set (and the per-slot
|
|
91
|
+
* `slots[slot]` override isn't), each slot resolves to this provider's
|
|
92
|
+
* `defaultModel` / `defaultFastModel`. This is the simple UI knob — bind it
|
|
93
|
+
* to a single "AI provider" dropdown.
|
|
94
|
+
*/
|
|
95
|
+
providerId?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Per-slot `(provider, model)` overrides. Wins over `providerId` when set.
|
|
98
|
+
* Useful for advanced configurations that pin a specific model per slot or
|
|
99
|
+
* mix providers across slots (e.g. Claude for `default`, a local model for
|
|
100
|
+
* `fast`).
|
|
92
101
|
*/
|
|
93
102
|
slots?: Partial<Record<SlotName, SlotConfig>>;
|
|
94
103
|
};
|