@narumitw/pi-btw 0.49.3 → 0.49.6

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
@@ -34,7 +34,7 @@ pi -e npm:@narumitw/pi-btw
34
34
  Try this package locally from the repository root:
35
35
 
36
36
  ```bash
37
- pi -e ./extensions/pi-btw
37
+ pi -e ./packages/pi-btw
38
38
  ```
39
39
 
40
40
  ## 🚀 Usage
@@ -154,7 +154,7 @@ Normal assistant messages become part of the main Pi conversation and can distra
154
154
  ## 🗂️ Package layout
155
155
 
156
156
  ```txt
157
- extensions/pi-btw/
157
+ packages/pi-btw/
158
158
  ├── src/
159
159
  │ ├── index.ts
160
160
  │ ├── btw.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-btw",
3
- "version": "0.49.3",
3
+ "version": "0.49.6",
4
4
  "description": "Pi extension that adds a /btw side-question command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,6 +22,9 @@
22
22
  "./src/index.ts"
23
23
  ]
24
24
  },
25
+ "piExtension": {
26
+ "lifecycle": "stable"
27
+ },
25
28
  "scripts": {
26
29
  "check": "biome check . && npm run typecheck",
27
30
  "format": "biome check --write .",
@@ -29,15 +32,15 @@
29
32
  },
30
33
  "devDependencies": {
31
34
  "@biomejs/biome": "2.5.7",
32
- "@earendil-works/pi-ai": "0.83.0",
33
- "@earendil-works/pi-coding-agent": "0.83.0",
34
- "@earendil-works/pi-tui": "0.83.0",
35
+ "@earendil-works/pi-ai": "0.84.0",
36
+ "@earendil-works/pi-coding-agent": "0.84.0",
37
+ "@earendil-works/pi-tui": "0.84.0",
35
38
  "typescript": "7.0.2"
36
39
  },
37
40
  "repository": {
38
41
  "type": "git",
39
42
  "url": "https://github.com/narumiruna/pi-extensions",
40
- "directory": "extensions/pi-btw"
43
+ "directory": "packages/pi-btw"
41
44
  },
42
45
  "dependencies": {
43
46
  "@narumitw/pi-tui-kit": "^0.49.1"
package/src/btw.ts CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  clampThinkingLevel,
4
4
  getSupportedThinkingLevels,
5
5
  type Model,
6
+ type ProviderHeaders,
6
7
  } from "@earendil-works/pi-ai";
7
8
  import {
8
9
  BorderedLoader,
@@ -39,6 +40,7 @@ import {
39
40
  import {
40
41
  BTW_THINKING_LEVELS,
41
42
  type BtwThinkingLevel,
43
+ type CompleteSimpleFunction,
42
44
  completeSideThreadTurn,
43
45
  createSideThread,
44
46
  type SideQuestionAuth,
@@ -65,7 +67,6 @@ export {
65
67
  type BtwThinkingLevel,
66
68
  buildUserPrompt,
67
69
  completeSideQuestion,
68
- loadCompleteSimple,
69
70
  } from "./side-thread.js";
70
71
  export { sanitizeSingleLine } from "./text.js";
71
72
 
@@ -76,14 +77,21 @@ interface LoadBtwThinkingLevelOptions {
76
77
  warn?: (message: string) => void;
77
78
  }
78
79
 
79
- interface BtwModelRegistry {
80
- find(provider: string, modelId: string): Model<Api> | undefined;
81
- getApiKeyAndHeaders(
82
- model: Model<Api>,
83
- ): Promise<
84
- | { ok: true; apiKey?: string; headers?: Record<string, string>; env?: Record<string, string> }
85
- | { ok: false; error: string }
86
- >;
80
+ type BtwModelRegistry = Pick<
81
+ ExtensionCommandContext["modelRegistry"],
82
+ "find" | "getApiKeyAndHeaders"
83
+ >;
84
+
85
+ type BtwProviderRegistry = Pick<ExtensionCommandContext["modelRegistry"], "getProvider">;
86
+
87
+ export function createModelRegistryCompleteSimple(
88
+ modelRegistry: BtwProviderRegistry,
89
+ ): CompleteSimpleFunction {
90
+ return async (model, context, options) => {
91
+ const provider = modelRegistry.getProvider(model.provider);
92
+ if (!provider) throw new Error(`No provider registered for model provider: ${model.provider}`);
93
+ return provider.streamSimple(model, context, options).result();
94
+ };
87
95
  }
88
96
 
89
97
  interface ResolveBtwModelOptions {
@@ -154,11 +162,15 @@ export async function resolveBtwModel({
154
162
  function hasRequestAuth(auth: SideQuestionAuth): boolean {
155
163
  return Boolean(
156
164
  auth.apiKey ||
157
- (auth.headers && Object.keys(auth.headers).length > 0) ||
165
+ providerHeadersHaveValue(auth.headers) ||
158
166
  (auth.env && Object.keys(auth.env).length > 0),
159
167
  );
160
168
  }
161
169
 
170
+ function providerHeadersHaveValue(headers: ProviderHeaders | undefined): boolean {
171
+ return headers !== undefined && Object.values(headers).some((value) => value !== null);
172
+ }
173
+
162
174
  export async function loadBtwThinkingLevel(
163
175
  currentThinkingLevel: BtwThinkingLevel,
164
176
  options: LoadBtwThinkingLevelOptions = {},
@@ -777,6 +789,7 @@ async function askThreadQuestion(
777
789
  thinkingLevel,
778
790
  auth: selected.auth,
779
791
  signal: view.signal,
792
+ completeSimple: createModelRegistryCompleteSimple(ctx.modelRegistry),
780
793
  }).then((result) => {
781
794
  if (settled) return;
782
795
  settled = true;
@@ -4,6 +4,7 @@ import type {
4
4
  Context,
5
5
  Message,
6
6
  Model,
7
+ ProviderHeaders,
7
8
  SimpleStreamOptions,
8
9
  UserMessage,
9
10
  } from "@earendil-works/pi-ai";
@@ -22,7 +23,7 @@ export type BtwThinkingLevel = (typeof BTW_THINKING_LEVELS)[number];
22
23
 
23
24
  export interface SideQuestionAuth {
24
25
  apiKey?: string;
25
- headers?: Record<string, string>;
26
+ headers?: ProviderHeaders;
26
27
  env?: Record<string, string>;
27
28
  }
28
29
 
@@ -32,36 +33,6 @@ export type CompleteSimpleFunction = <TApi extends Api>(
32
33
  options?: SimpleStreamOptions,
33
34
  ) => Promise<AssistantMessage>;
34
35
 
35
- type ModuleImporter = (moduleId: string) => Promise<unknown>;
36
-
37
- function hasCompleteSimple(value: unknown): value is { completeSimple: CompleteSimpleFunction } {
38
- return (
39
- typeof value === "object" &&
40
- value !== null &&
41
- typeof Reflect.get(value, "completeSimple") === "function"
42
- );
43
- }
44
-
45
- export async function loadCompleteSimple(
46
- importModule: ModuleImporter = (moduleId) => import(moduleId),
47
- ): Promise<CompleteSimpleFunction> {
48
- let importError: unknown;
49
- for (const moduleId of ["@earendil-works/pi-ai/compat", "@earendil-works/pi-ai"]) {
50
- try {
51
- const module = await importModule(moduleId);
52
- if (hasCompleteSimple(module)) return module.completeSimple;
53
- } catch (error: unknown) {
54
- importError = error;
55
- }
56
- }
57
-
58
- throw new Error("@earendil-works/pi-ai does not export completeSimple", {
59
- cause: importError,
60
- });
61
- }
62
-
63
- const defaultCompleteSimple = await loadCompleteSimple();
64
-
65
36
  export type SideThreadTurn =
66
37
  | {
67
38
  kind: "answered";
@@ -114,7 +85,7 @@ export interface CompleteSideThreadTurnOptions {
114
85
  thinkingLevel: BtwThinkingLevel;
115
86
  auth: SideQuestionAuth;
116
87
  signal?: AbortSignal;
117
- completeSimple?: CompleteSimpleFunction;
88
+ completeSimple: CompleteSimpleFunction;
118
89
  }
119
90
 
120
91
  export type CompleteSideThreadTurnResult =
@@ -129,7 +100,7 @@ export async function completeSideThreadTurn({
129
100
  thinkingLevel,
130
101
  auth,
131
102
  signal,
132
- completeSimple = defaultCompleteSimple,
103
+ completeSimple,
133
104
  }: CompleteSideThreadTurnOptions): Promise<CompleteSideThreadTurnResult> {
134
105
  if (signal?.aborted) return { kind: "aborted" };
135
106
  let response: AssistantMessage;
@@ -161,7 +132,7 @@ export interface CompleteSideQuestionOptions {
161
132
  thinkingLevel: BtwThinkingLevel;
162
133
  auth: SideQuestionAuth;
163
134
  signal?: AbortSignal;
164
- completeSimple?: CompleteSimpleFunction;
135
+ completeSimple: CompleteSimpleFunction;
165
136
  }
166
137
 
167
138
  export async function completeSideQuestion({
@@ -171,7 +142,7 @@ export async function completeSideQuestion({
171
142
  thinkingLevel,
172
143
  auth,
173
144
  signal,
174
- completeSimple = defaultCompleteSimple,
145
+ completeSimple,
175
146
  }: CompleteSideQuestionOptions): Promise<AssistantMessage> {
176
147
  return completeSimple(
177
148
  model,