pi-cliproxyapi-provider 0.1.0 → 0.3.0
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 +20 -10
- package/data/models-dev-fallback.json +1 -1
- package/extensions/index.ts +26 -9
- package/package.json +27 -8
- package/src/cache.ts +12 -34
- package/src/catalog.ts +185 -0
- package/src/commands.ts +80 -67
- package/src/config.ts +0 -9
- package/src/cpa.ts +21 -7
- package/src/discovery.ts +2 -82
- package/src/model-capabilities.ts +54 -0
- package/src/models-dev.ts +3 -0
- package/src/provider.ts +13 -1
- package/src/runtime.ts +41 -0
- package/src/types.ts +3 -2
package/src/discovery.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { cacheDir, providerCacheKey
|
|
3
|
-
import {
|
|
4
|
-
import { getCachedOrFetch, isFresh, readCache } from "./cache.ts";
|
|
5
|
-
import { fetchModelsDevCatalog, readBundledModelsDevFallback } from "./models-dev.ts";
|
|
6
|
-
import type { ModelsDevCatalog } from "./types.ts";
|
|
7
|
-
|
|
8
|
-
export interface DiscoveryResult {
|
|
9
|
-
cpaModels: CpaModel[];
|
|
10
|
-
modelsDevCatalog: ModelsDevCatalog;
|
|
11
|
-
sources: {
|
|
12
|
-
cpa: "fresh" | "cache" | "stale";
|
|
13
|
-
modelsDev: "fresh" | "cache" | "stale" | "bundled" | "disabled";
|
|
14
|
-
};
|
|
15
|
-
errors: {
|
|
16
|
-
cpa?: unknown;
|
|
17
|
-
modelsDev?: unknown;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
2
|
+
import { cacheDir, providerCacheKey } from "./config.ts";
|
|
3
|
+
import type { CpaProviderConfig } from "./types.ts";
|
|
20
4
|
|
|
21
5
|
export function cpaModelsCachePath(config: CpaProviderConfig): string {
|
|
22
6
|
return join(cacheDir(), providerCacheKey(config), "cpa-models.json");
|
|
@@ -32,67 +16,3 @@ export function discoveryHeaders(config: CpaProviderConfig, apiKey?: string): Re
|
|
|
32
16
|
...(config.authHeader && apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
33
17
|
};
|
|
34
18
|
}
|
|
35
|
-
|
|
36
|
-
export async function discoverModels(options: {
|
|
37
|
-
config: CpaProviderConfig;
|
|
38
|
-
bundledModelsDevPath: string;
|
|
39
|
-
force?: boolean;
|
|
40
|
-
discoveryApiKey?: string;
|
|
41
|
-
}): Promise<DiscoveryResult> {
|
|
42
|
-
const cpa = await getCachedOrFetch({
|
|
43
|
-
path: cpaModelsCachePath(options.config),
|
|
44
|
-
ttlSeconds: options.config.cpaCacheTtlSeconds,
|
|
45
|
-
force: options.force,
|
|
46
|
-
fetchFresh: () => fetchCpaModels(options.config.baseUrl, discoveryHeaders(options.config, options.discoveryApiKey)),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (!options.config.modelsDevEnabled) {
|
|
50
|
-
return {
|
|
51
|
-
cpaModels: cpa.data,
|
|
52
|
-
modelsDevCatalog: {},
|
|
53
|
-
sources: { cpa: cpa.source, modelsDev: "disabled" },
|
|
54
|
-
errors: { cpa: cpa.error },
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!options.force) {
|
|
59
|
-
const cachedModelsDev = await readCache<ModelsDevCatalog>(modelsDevCachePath());
|
|
60
|
-
if (isFresh(cachedModelsDev, options.config.modelsDevCacheTtlSeconds)) {
|
|
61
|
-
return {
|
|
62
|
-
cpaModels: cpa.data,
|
|
63
|
-
modelsDevCatalog: cachedModelsDev.data,
|
|
64
|
-
sources: { cpa: cpa.source, modelsDev: "cache" },
|
|
65
|
-
errors: { cpa: cpa.error },
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
cpaModels: cpa.data,
|
|
71
|
-
modelsDevCatalog: await readBundledModelsDevFallback(options.bundledModelsDevPath),
|
|
72
|
-
sources: { cpa: cpa.source, modelsDev: "bundled" },
|
|
73
|
-
errors: { cpa: cpa.error },
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
const modelsDev = await getCachedOrFetch({
|
|
79
|
-
path: modelsDevCachePath(),
|
|
80
|
-
ttlSeconds: options.config.modelsDevCacheTtlSeconds,
|
|
81
|
-
force: true,
|
|
82
|
-
fetchFresh: fetchModelsDevCatalog,
|
|
83
|
-
});
|
|
84
|
-
return {
|
|
85
|
-
cpaModels: cpa.data,
|
|
86
|
-
modelsDevCatalog: modelsDev.data,
|
|
87
|
-
sources: { cpa: cpa.source, modelsDev: modelsDev.source },
|
|
88
|
-
errors: { cpa: cpa.error, modelsDev: modelsDev.error },
|
|
89
|
-
};
|
|
90
|
-
} catch (error) {
|
|
91
|
-
return {
|
|
92
|
-
cpaModels: cpa.data,
|
|
93
|
-
modelsDevCatalog: await readBundledModelsDevFallback(options.bundledModelsDevPath),
|
|
94
|
-
sources: { cpa: cpa.source, modelsDev: "bundled" },
|
|
95
|
-
errors: { cpa: cpa.error, modelsDev: error },
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ThinkingLevelMap } from "@earendil-works/pi-ai";
|
|
2
|
+
|
|
3
|
+
export interface ModelCapabilityContext {
|
|
4
|
+
availableModelId: string;
|
|
5
|
+
metadataModelId?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ModelCapabilityOverrides {
|
|
9
|
+
reasoning?: boolean;
|
|
10
|
+
thinkingLevelMap?: ThinkingLevelMap;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ModelCapabilityRule {
|
|
14
|
+
matches: (context: ModelCapabilityContext) => boolean;
|
|
15
|
+
overrides: ModelCapabilityOverrides;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const GPT_5_6_THINKING_LEVEL_MAP: ThinkingLevelMap = {
|
|
19
|
+
off: "none",
|
|
20
|
+
minimal: "minimal",
|
|
21
|
+
low: "low",
|
|
22
|
+
medium: "medium",
|
|
23
|
+
high: "high",
|
|
24
|
+
xhigh: "xhigh",
|
|
25
|
+
max: "max",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function includesModelFamily(context: ModelCapabilityContext, family: string): boolean {
|
|
29
|
+
return [context.availableModelId, context.metadataModelId]
|
|
30
|
+
.filter((id): id is string => id !== undefined)
|
|
31
|
+
.some((id) => id.includes(family));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const MODEL_CAPABILITY_RULES: readonly ModelCapabilityRule[] = [
|
|
35
|
+
{
|
|
36
|
+
matches: (context) => includesModelFamily(context, "gpt-5.6"),
|
|
37
|
+
overrides: {
|
|
38
|
+
reasoning: true,
|
|
39
|
+
thinkingLevelMap: GPT_5_6_THINKING_LEVEL_MAP,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export function getModelCapabilityOverrides(context: ModelCapabilityContext): ModelCapabilityOverrides {
|
|
45
|
+
const resolved: ModelCapabilityOverrides = {};
|
|
46
|
+
|
|
47
|
+
for (const rule of MODEL_CAPABILITY_RULES) {
|
|
48
|
+
if (!rule.matches(context)) continue;
|
|
49
|
+
if (rule.overrides.reasoning !== undefined) resolved.reasoning = rule.overrides.reasoning;
|
|
50
|
+
if (rule.overrides.thinkingLevelMap) resolved.thinkingLevelMap = { ...rule.overrides.thinkingLevelMap };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return resolved;
|
|
54
|
+
}
|
package/src/models-dev.ts
CHANGED
|
@@ -32,6 +32,9 @@ export function parseModelsDevCatalog(payload: unknown): ModelsDevCatalog {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
if (Object.keys(catalog).length === 0 && Object.keys(record).length > 0) {
|
|
36
|
+
throw new Error("models.dev catalog contained no valid models");
|
|
37
|
+
}
|
|
35
38
|
return catalog;
|
|
36
39
|
}
|
|
37
40
|
|
package/src/provider.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CpaModel } from "./cpa.ts";
|
|
2
2
|
import { findMetadataMatch, type MetadataMatchMethod } from "./matching.ts";
|
|
3
|
+
import { getModelCapabilityOverrides } from "./model-capabilities.ts";
|
|
3
4
|
import type { InputModality, ModelsDevCatalog, ModelsDevMetadata, ProviderModelConfigLike } from "./types.ts";
|
|
4
5
|
|
|
5
6
|
export const PI_MODEL_DEFAULTS = {
|
|
@@ -38,10 +39,18 @@ function costFromMetadata(metadata: ModelsDevMetadata): ProviderModelConfigLike[
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function modelFromMetadata(cpaModel: CpaModel, metadata: ModelsDevMetadata): ProviderModelConfigLike {
|
|
42
|
+
const capabilityOverrides = getModelCapabilityOverrides({
|
|
43
|
+
availableModelId: cpaModel.id,
|
|
44
|
+
metadataModelId: metadata.id,
|
|
45
|
+
});
|
|
46
|
+
|
|
41
47
|
return {
|
|
42
48
|
id: cpaModel.id,
|
|
43
49
|
name: metadata.name ?? cpaModel.id,
|
|
44
|
-
reasoning: metadata.reasoning ?? PI_MODEL_DEFAULTS.reasoning,
|
|
50
|
+
reasoning: capabilityOverrides.reasoning ?? metadata.reasoning ?? PI_MODEL_DEFAULTS.reasoning,
|
|
51
|
+
...(capabilityOverrides.thinkingLevelMap
|
|
52
|
+
? { thinkingLevelMap: capabilityOverrides.thinkingLevelMap }
|
|
53
|
+
: {}),
|
|
45
54
|
input: inputFromMetadata(metadata),
|
|
46
55
|
cost: costFromMetadata(metadata),
|
|
47
56
|
contextWindow: metadata.limit?.context ?? PI_MODEL_DEFAULTS.contextWindow,
|
|
@@ -58,10 +67,13 @@ function cloneModelDefaults(): typeof PI_MODEL_DEFAULTS {
|
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
function defaultModel(cpaModel: CpaModel): ProviderModelConfigLike {
|
|
70
|
+
const capabilityOverrides = getModelCapabilityOverrides({ availableModelId: cpaModel.id });
|
|
71
|
+
|
|
61
72
|
return {
|
|
62
73
|
id: cpaModel.id,
|
|
63
74
|
name: cpaModel.id,
|
|
64
75
|
...cloneModelDefaults(),
|
|
76
|
+
...capabilityOverrides,
|
|
65
77
|
};
|
|
66
78
|
}
|
|
67
79
|
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { ProviderCatalog, type CatalogRefreshResult, type CatalogSnapshot, type RefreshTarget } from "./catalog.ts";
|
|
3
|
+
import { buildUnavailableProviderModels } from "./provider.ts";
|
|
4
|
+
import { buildProviderRegistration } from "./registration.ts";
|
|
5
|
+
import type { CpaProviderConfig } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
export interface ProviderRuntimeOptions {
|
|
8
|
+
pi: ExtensionAPI;
|
|
9
|
+
config: CpaProviderConfig;
|
|
10
|
+
catalog: ProviderCatalog;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ProviderRuntime {
|
|
14
|
+
private registeredFingerprint?: string;
|
|
15
|
+
private readonly options: ProviderRuntimeOptions;
|
|
16
|
+
|
|
17
|
+
constructor(options: ProviderRuntimeOptions) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async start(): Promise<CatalogSnapshot> {
|
|
22
|
+
const snapshot = await this.options.catalog.load();
|
|
23
|
+
this.register(snapshot, true);
|
|
24
|
+
return snapshot;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async refresh(target: RefreshTarget = "all", mode: "background" | "manual" = "manual"): Promise<CatalogRefreshResult> {
|
|
28
|
+
const result = await this.options.catalog.refresh(target, mode);
|
|
29
|
+
if (result.models.updated || result.metadata.updated) this.register(result.snapshot, false);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private register(snapshot: CatalogSnapshot, force: boolean): void {
|
|
34
|
+
const models = snapshot.built.models.length > 0 ? snapshot.built.models : buildUnavailableProviderModels();
|
|
35
|
+
const fingerprint = JSON.stringify(models);
|
|
36
|
+
if (!force && fingerprint === this.registeredFingerprint) return;
|
|
37
|
+
const registration = buildProviderRegistration(this.options.config, models);
|
|
38
|
+
this.options.pi.registerProvider(registration.providerName, registration.config);
|
|
39
|
+
this.registeredFingerprint = fingerprint;
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ThinkingLevelMap } from "@earendil-works/pi-ai";
|
|
2
|
+
|
|
1
3
|
export type InputModality = "text" | "image";
|
|
2
4
|
|
|
3
5
|
export interface CpaProviderConfig {
|
|
@@ -6,8 +8,6 @@ export interface CpaProviderConfig {
|
|
|
6
8
|
authRequired: boolean;
|
|
7
9
|
authHeader: boolean;
|
|
8
10
|
headers: Record<string, string>;
|
|
9
|
-
cpaCacheTtlSeconds: number;
|
|
10
|
-
modelsDevCacheTtlSeconds: number;
|
|
11
11
|
modelsDevEnabled: boolean;
|
|
12
12
|
modelAliases: Record<string, string>;
|
|
13
13
|
}
|
|
@@ -38,6 +38,7 @@ export interface ProviderModelConfigLike {
|
|
|
38
38
|
id: string;
|
|
39
39
|
name: string;
|
|
40
40
|
reasoning: boolean;
|
|
41
|
+
thinkingLevelMap?: ThinkingLevelMap;
|
|
41
42
|
input: InputModality[];
|
|
42
43
|
cost: {
|
|
43
44
|
input: number;
|