pi-vision-handoff 0.8.1 → 0.8.2

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.
Files changed (2) hide show
  1. package/package.json +9 -5
  2. package/src/describer.ts +36 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-vision-handoff",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Give text-only pi models vision — describe images with a vision model you pick via an interactive picker, then hand off the text description to non-vision models",
5
5
  "type": "module",
6
6
  "author": "Tom X Nguyen",
@@ -31,9 +31,9 @@
31
31
  "README.md"
32
32
  ],
33
33
  "devDependencies": {
34
- "@earendil-works/pi-ai": "0.80.6",
35
- "@earendil-works/pi-coding-agent": "0.80.6",
36
- "@earendil-works/pi-tui": "0.80.6",
34
+ "@earendil-works/pi-ai": "0.80.10",
35
+ "@earendil-works/pi-coding-agent": "0.80.10",
36
+ "@earendil-works/pi-tui": "0.80.10",
37
37
  "@types/node": "25.9.1",
38
38
  "@vitest/coverage-v8": "4.1.7",
39
39
  "knip": "6.14.1",
@@ -45,7 +45,11 @@
45
45
  "./vision-handoff.ts"
46
46
  ]
47
47
  },
48
- "peerDependencies": {},
48
+ "peerDependencies": {
49
+ "@earendil-works/pi-ai": "*",
50
+ "@earendil-works/pi-coding-agent": "*",
51
+ "@earendil-works/pi-tui": "*"
52
+ },
49
53
  "scripts": {
50
54
  "test": "vitest run",
51
55
  "test:watch": "vitest",
package/src/describer.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The vision describer: calls a vision-capable model via pi-ai's
3
- * `completeSimple()` to produce text descriptions of images.
2
+ * The vision describer calls a vision-capable model through its registered
3
+ * provider stream, falling back to pi-ai's `completeSimple()` for built-ins.
4
4
  *
5
5
  * `completeSimple` (not `complete`) is the path that translates the `reasoning`
6
6
  * ThinkingLevel into each provider's `reasoningEffort`/budget. `complete()` →
@@ -26,7 +26,17 @@
26
26
  * abort listener.
27
27
  */
28
28
 
29
- import type { Api, ImageContent, Message, Model, TextContent, ThinkingLevel } from "@earendil-works/pi-ai";
29
+ import type {
30
+ Api,
31
+ AssistantMessage,
32
+ Context,
33
+ ImageContent,
34
+ Message,
35
+ Model,
36
+ SimpleStreamOptions,
37
+ TextContent,
38
+ ThinkingLevel,
39
+ } from "@earendil-works/pi-ai";
30
40
  import { completeSimple } from "@earendil-works/pi-ai/compat";
31
41
  import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
32
42
  import {
@@ -117,6 +127,25 @@ function configSnapshot(cfg: VisionHandoffConfig): {
117
127
  return { maxTokens: cfg.maxTokens, thinking: cfg.thinking, thinkingLevel: cfg.thinkingLevel };
118
128
  }
119
129
 
130
+ /** Complete through the provider registered in Pi's model registry when one
131
+ * supplies a custom stream. The post-0.80 ModelRuntime keeps extension streams
132
+ * out of pi-ai's deprecated global compatibility registry, so calling
133
+ * completeSimple() directly cannot resolve custom API ids such as `makora`.
134
+ * The optional access preserves compatibility with older ModelRegistry versions,
135
+ * which registered custom streams globally and do not expose the config getter. */
136
+ export async function completeVisionModel(
137
+ model: Model<Api>,
138
+ modelRegistry: ModelRegistry,
139
+ context: Context,
140
+ options: SimpleStreamOptions,
141
+ ): Promise<AssistantMessage> {
142
+ const provider = modelRegistry.getRegisteredProviderConfig?.(model.provider);
143
+ if (provider?.streamSimple && provider.api === model.api) {
144
+ return provider.streamSimple(model, context, options).result();
145
+ }
146
+ return completeSimple(model, context, options);
147
+ }
148
+
120
149
  /** Dependencies the describer can't own itself (held by the engine). */
121
150
  export interface DescriberDeps {
122
151
  /** Report a usage+energy record for one real describer call (cache hits emit none). */
@@ -189,8 +218,9 @@ export async function runBatch(
189
218
  const describeCtx: DescribeContext = { energyReader: undefined };
190
219
  try {
191
220
  const response = await describeAls.run(describeCtx, async () =>
192
- completeSimple(
221
+ completeVisionModel(
193
222
  visionModel,
223
+ modelRegistry,
194
224
  { systemPrompt, messages: [userMessage] },
195
225
  { apiKey: auth.apiKey, headers: auth.headers, signal: controller.signal, maxTokens, reasoning },
196
226
  ),
@@ -360,8 +390,9 @@ export async function describeSingle(
360
390
  const describeCtx: DescribeContext = { energyReader: undefined };
361
391
  try {
362
392
  const response = await describeAls.run(describeCtx, async () =>
363
- completeSimple(
393
+ completeVisionModel(
364
394
  visionModel,
395
+ modelRegistry,
365
396
  { systemPrompt, messages: [userMessage] },
366
397
  { apiKey: auth.apiKey, headers: auth.headers, signal: controller.signal, maxTokens, reasoning },
367
398
  ),