pi-para 0.6.5 → 0.6.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.
@@ -3,15 +3,22 @@ import type { ProviderCredentialRef, ParaUserConfig } from "./config.js";
3
3
  import { resolveCredentialRef } from "./credentials.js";
4
4
 
5
5
  export interface ModelRegistryLike {
6
+ getAll(): Model<any>[];
6
7
  getAvailable(): Model<any>[];
7
8
  find(provider: string, modelId: string): Model<any> | undefined;
8
9
  getApiKeyForProvider(provider: string): Promise<string | undefined>;
9
10
  }
10
11
 
11
- export interface AuthStorageLike {
12
+ export interface PiCredentialReader {
13
+ hasStoredCredential(provider: string): boolean;
12
14
  getApiKey(provider: string): Promise<string | undefined>;
13
15
  }
14
16
 
17
+ export interface PiModelServices {
18
+ modelRegistry: ModelRegistryLike;
19
+ credentials: PiCredentialReader;
20
+ }
21
+
15
22
  export type ModelSelection = ProviderCredentialRef | "auto";
16
23
 
17
24
  export function parseProviderModelSpec(spec: string): { provider: string; modelId: string } | null {
@@ -20,6 +27,10 @@ export function parseProviderModelSpec(spec: string): { provider: string; modelI
20
27
  return { provider: spec.slice(0, idx), modelId: spec.slice(idx + 1) };
21
28
  }
22
29
 
30
+ function isRemoteModel(model: Model<any>): boolean {
31
+ return model.provider !== "node-llama-cpp" && model.provider !== "local" && !model.provider.includes("llama");
32
+ }
33
+
23
34
  export function resolveSelectedModel(
24
35
  selection: ModelSelection,
25
36
  registry: ModelRegistryLike,
@@ -27,47 +38,50 @@ export function resolveSelectedModel(
27
38
  ): Model<any> | null {
28
39
  if (selection !== "auto") {
29
40
  if (selection.model) return registry.find(selection.provider, selection.model) ?? null;
30
- return registry.getAvailable().find((model) => model.provider === selection.provider) ?? null;
41
+ return registry.getAvailable().find((model) => model.provider === selection.provider && isRemoteModel(model)) ?? null;
31
42
  }
32
43
 
33
44
  const legacy = opts.legacyModelSpec ? parseProviderModelSpec(opts.legacyModelSpec) : null;
34
45
  if (legacy) {
35
46
  const model = registry.find(legacy.provider, legacy.modelId);
36
- if (model) return model;
47
+ if (model && isRemoteModel(model)) return model;
37
48
  }
38
49
 
39
50
  const preferred = opts.preferredModelSpec ? parseProviderModelSpec(opts.preferredModelSpec) : null;
40
51
  if (preferred) {
41
52
  const model = registry.find(preferred.provider, preferred.modelId);
42
- if (model) return model;
53
+ if (model && isRemoteModel(model)) return model;
43
54
  }
44
55
 
45
- return pickBestAvailableModel(registry.getAvailable());
56
+ return pickBestAvailableModel(registry.getAvailable().filter(isRemoteModel));
46
57
  }
47
58
 
48
59
  export function pickBestAvailableModel(models: Model<any>[]): Model<any> | null {
49
- if (models.length === 0) return null;
50
- const sonnet = models.find((model) => model.provider === "anthropic" && /sonnet/i.test(model.id));
60
+ const remote = models.filter(isRemoteModel);
61
+ if (remote.length === 0) return null;
62
+ const sonnet = remote.find((model) => model.provider === "anthropic" && /sonnet/i.test(model.id));
51
63
  if (sonnet) return sonnet;
52
- return [...models].sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0))[0] ?? null;
64
+ return [...remote].sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0))[0] ?? null;
53
65
  }
54
66
 
55
67
  export function createModelApiKeyResolver(
56
68
  selection: ModelSelection,
57
69
  registry: ModelRegistryLike,
58
- opts: { authStorage?: AuthStorageLike; secretsPath?: string } = {},
70
+ opts: { credentials?: PiCredentialReader; secretsPath?: string } = {},
59
71
  ): (provider: string) => Promise<string | undefined> {
60
72
  return async (provider: string) => {
61
- if (selection === "auto") return registry.getApiKeyForProvider(provider);
73
+ if (selection === "auto") {
74
+ return opts.credentials ? opts.credentials.getApiKey(provider) : registry.getApiKeyForProvider(provider);
75
+ }
76
+ if (selection.provider !== provider) return undefined;
62
77
 
63
78
  if (selection.credentialRef.startsWith("pi-auth:")) {
64
79
  const configuredProvider = selection.credentialRef.slice("pi-auth:".length);
65
- if (configuredProvider !== provider && selection.provider !== provider) return undefined;
66
- return (await opts.authStorage?.getApiKey(provider)) ?? await registry.getApiKeyForProvider(provider);
80
+ if (configuredProvider !== provider) return undefined;
81
+ return opts.credentials ? opts.credentials.getApiKey(provider) : registry.getApiKeyForProvider(provider);
67
82
  }
68
83
 
69
84
  if (selection.credentialRef.startsWith("secret:")) {
70
- if (selection.provider !== provider) return undefined;
71
85
  const resolved = await resolveCredentialRef(selection.credentialRef, { secretsPath: opts.secretsPath });
72
86
  return resolved.ok ? resolved.value : undefined;
73
87
  }
@@ -80,17 +94,28 @@ export function getCaptureSelection(config: ParaUserConfig): ModelSelection {
80
94
  return config.models.capture;
81
95
  }
82
96
 
83
- export async function createPiModelRegistry(): Promise<{ authStorage: AuthStorageLike; modelRegistry: ModelRegistryLike } | null> {
97
+ export async function createPiModelServices(
98
+ options: { authPath?: string } = {},
99
+ ): Promise<PiModelServices | null> {
84
100
  try {
85
101
  const mod = await import("@earendil-works/pi-coding-agent");
86
- const typed = mod as unknown as {
87
- AuthStorage?: { create(): AuthStorageLike };
88
- ModelRegistry?: { create(authStorage: AuthStorageLike): ModelRegistryLike };
102
+ const runtime = await mod.ModelRuntime.create({ authPath: options.authPath, allowModelNetwork: false });
103
+ const modelRegistry = new mod.ModelRegistry(runtime);
104
+ await modelRegistry.refresh();
105
+ return {
106
+ modelRegistry,
107
+ credentials: {
108
+ hasStoredCredential: (provider) => Boolean(mod.readStoredCredential(provider, options.authPath)),
109
+ getApiKey: async (provider) => {
110
+ if (!mod.readStoredCredential(provider, options.authPath)) return undefined;
111
+ return (await runtime.getAuth(provider))?.auth.apiKey;
112
+ },
113
+ },
89
114
  };
90
- if (!typed.AuthStorage || !typed.ModelRegistry) return null;
91
- const authStorage = typed.AuthStorage.create();
92
- return { authStorage, modelRegistry: typed.ModelRegistry.create(authStorage) };
93
115
  } catch {
94
116
  return null;
95
117
  }
96
118
  }
119
+
120
+ /** @deprecated Use createPiModelServices. */
121
+ export const createPiModelRegistry = createPiModelServices;