@inkdropapp/ai 0.1.0 → 0.1.1

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 CHANGED
@@ -44,10 +44,8 @@ const settings: AISettings = {
44
44
  }
45
45
  ]
46
46
  },
47
- slots: {
48
- default: { providerId: 'anthropic', modelId: 'claude-sonnet-4-6' },
49
- fast: { providerId: 'anthropic', modelId: 'claude-haiku-4-5' }
50
- }
47
+ // Pick one provider — slot models come from its catalogue defaults.
48
+ providerId: 'anthropic'
51
49
  }
52
50
 
53
51
  const registry = new Registry({ settings })
@@ -102,13 +100,18 @@ Inkdrop's AI features map onto two slots:
102
100
 
103
101
  A slot resolves to `(provider, model)` via:
104
102
 
105
- 1. Explicit binding from `settings.slots[slot]`, if both the provider id and model id resolve.
106
- 2. The first registered provider's `defaultModel()` (for `default`) or `defaultFastModel()` (for `fast`).
107
- 3. `undefined` if no provider can satisfy the slot.
108
-
109
- Authentication is **not** checked during slot resolution that's
110
- `streamCompletion`'s job, which throws `NoApiKey` if no provider is configured
111
- at all and surfaces upstream auth failures inline as `'error'` events.
103
+ 1. Explicit binding from `settings.slots[slot]`, if both the provider id and model id resolve. *(Advanced — pin a specific model per slot or mix providers across slots.)*
104
+ 2. For `'fast'` only: fall back to `settings.slots.default` if that resolves.
105
+ 3. Preferred provider from `settings.providerId`, if configured — takes its `defaultModel()` (for `default`) or `defaultFastModel()` (for `fast`). *(The simple UI knob — one provider dropdown drives every slot.)*
106
+ 4. First **authenticated** registered provider in alphabetical id order, taking its `defaultModel()` / `defaultFastModel()`.
107
+ 5. `undefined` if no provider can satisfy the slot.
108
+
109
+ Steps 1 and 3 do **not** check authentication the user picked those
110
+ explicitly, so auth errors surface inline as `'error'` events at stream time
111
+ rather than silently swapping providers. Step 4 is a courtesy fallback for
112
+ hosts that don't wire any preference at all; it skips unauthenticated
113
+ providers. `streamCompletion` throws `NoApiKey` only when resolution returns
114
+ `undefined` (i.e. no provider is configured at all).
112
115
 
113
116
  ### Credential resolution
114
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkdropapp/ai",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "AI integration common module",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -19,11 +19,11 @@
19
19
  "author": "Takuya Matsuyama <t@inkdrop.app>",
20
20
  "license": "UNLICENSED",
21
21
  "dependencies": {
22
- "@ai-sdk/anthropic": "^4.0.0-beta.40",
23
- "@ai-sdk/openai-compatible": "^3.0.0-beta.33",
22
+ "@ai-sdk/anthropic": "4.0.0-beta.42",
23
+ "@ai-sdk/openai-compatible": "3.0.0-beta.36",
24
24
  "@inkdropapp/ai-catalog": "^0.1.0",
25
25
  "@napi-rs/keyring": "^1.2.0",
26
- "ai": "^7.0.0-beta.113"
26
+ "ai": "7.0.0-beta.116"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/jest": "^30.0.0",
package/src/provider.ts CHANGED
@@ -52,7 +52,10 @@ export interface AIModel {
52
52
  * OpenAI-compatible providers (OpenRouter, Together, Ollama, …) are modelled.
53
53
  */
54
54
  export interface AIProvider {
55
- /** Stable provider id; appears in `AISettings.slots[*].providerId`. */
55
+ /**
56
+ * Stable provider id; appears in both `AISettings.providerId` and
57
+ * `AISettings.slots[*].providerId`.
58
+ */
56
59
  readonly id: string
57
60
  /** Human-readable label. */
58
61
  readonly name: string
package/src/registry.ts CHANGED
@@ -84,12 +84,17 @@ export class Registry {
84
84
  * Order:
85
85
  * 1. Explicit binding `settings.slots[slot]`, if both provider and model resolve.
86
86
  * 2. For `'fast'` only: fall back to `settings.slots.default`.
87
- * 3. First authenticated provider in alphabetical id order, taking that
87
+ * 3. Preferred provider `settings.providerId`, if that provider is configured,
88
+ * taking its `defaultModel()` (or `defaultFastModel()` for the fast slot).
89
+ * Authentication is not gated here — the user explicitly picked this
90
+ * provider; auth errors surface at stream time rather than silently
91
+ * swapping providers.
92
+ * 4. First authenticated provider in alphabetical id order, taking that
88
93
  * provider's `defaultModel()` (or `defaultFastModel()` for the fast slot).
89
94
  * Mirrors Zed's `available_fallback_model` without the Zed-Cloud preference.
90
- * 4. `undefined` if no provider can satisfy the slot.
95
+ * 5. `undefined` if no provider can satisfy the slot.
91
96
  *
92
- * Async because the third step calls `provider.isAuthenticated()`, which may
97
+ * Async because the fourth step calls `provider.isAuthenticated()`, which may
93
98
  * touch the keyring.
94
99
  */
95
100
  async resolveSlot(slot: SlotName): Promise<ResolvedSlot | undefined> {
@@ -101,6 +106,17 @@ export class Registry {
101
106
  if (fallback) return fallback
102
107
  }
103
108
 
109
+ if (this.settings.providerId) {
110
+ const provider = this.providers.get(this.settings.providerId)
111
+ if (provider) {
112
+ const model =
113
+ slot === 'fast'
114
+ ? provider.defaultFastModel()
115
+ : provider.defaultModel()
116
+ if (model) return { provider, model }
117
+ }
118
+ }
119
+
104
120
  for (const provider of this.providers.values()) {
105
121
  if (!(await provider.isAuthenticated())) continue
106
122
  const model =
package/src/settings.ts CHANGED
@@ -98,8 +98,17 @@ export type AISettings = {
98
98
  openaiCompatible?: OpenAICompatibleProviderConfig[]
99
99
  }
100
100
  /**
101
- * Per-slot model overrides. Unset slots fall back to the first registered
102
- * provider's `defaultModel` / `defaultFastModel`.
101
+ * Preferred provider for every task slot. When set (and the per-slot
102
+ * `slots[slot]` override isn't), each slot resolves to this provider's
103
+ * `defaultModel` / `defaultFastModel`. This is the simple UI knob — bind it
104
+ * to a single "AI provider" dropdown.
105
+ */
106
+ providerId?: string
107
+ /**
108
+ * Per-slot `(provider, model)` overrides. Wins over `providerId` when set.
109
+ * Useful for advanced configurations that pin a specific model per slot or
110
+ * mix providers across slots (e.g. Claude for `default`, a local model for
111
+ * `fast`).
103
112
  */
104
113
  slots?: Partial<Record<SlotName, SlotConfig>>
105
114
  }