decorated-pi 0.1.0 → 0.2.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/extensions/index.ts +2 -0
- package/extensions/providers/ark-coding.ts +73 -0
- package/extensions/providers/index.ts +9 -0
- package/extensions/providers/ollama-cloud.ts +101 -0
- package/extensions/providers/qianfan-coding.ts +71 -0
- package/extensions/safety.ts +1 -0
- package/extensions/settings.ts +38 -1
- package/package.json +2 -1
package/extensions/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { setupSubdirAgents } from "./subdir-agents";
|
|
|
10
10
|
import { setupSessionTitle } from "./session-title";
|
|
11
11
|
import { setupGuidance } from "./guidance";
|
|
12
12
|
import { setupLsp } from "./lsp/index";
|
|
13
|
+
import { setupProviders } from "./providers/index";
|
|
13
14
|
import { setupSmartAt } from "./smart-at";
|
|
14
15
|
|
|
15
16
|
export default function (pi: ExtensionAPI) {
|
|
@@ -20,5 +21,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
20
21
|
setupSessionTitle(pi);
|
|
21
22
|
setupGuidance(pi);
|
|
22
23
|
setupLsp(pi);
|
|
24
|
+
setupProviders(pi);
|
|
23
25
|
setupSmartAt(pi);
|
|
24
26
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARK Coding Plan — OAuth/subscription provider with hardcoded models
|
|
3
|
+
*
|
|
4
|
+
* Provider: "ark-coding"
|
|
5
|
+
* Base URL: https://ark.cn-beijing.volces.com/api/coding/v3 (OpenAI-compatible)
|
|
6
|
+
* Auth: OAuth/subscription login → prompt for API key
|
|
7
|
+
*
|
|
8
|
+
* All models hardcoded. No dynamic fetching, no config file caching.
|
|
9
|
+
* - No auth → no models in /model (clean UX, via hasConfiguredAuth)
|
|
10
|
+
* - Login → models become available immediately
|
|
11
|
+
* - Startup → models registered unconditionally (hardcoded)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionAPI, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
|
|
16
|
+
|
|
17
|
+
const PROVIDER_ID = "ark-coding";
|
|
18
|
+
const PROVIDER_DISPLAY_NAME = "ARK Coding Plan";
|
|
19
|
+
const BASE_URL = "https://ark.cn-beijing.volces.com/api/coding/v3";
|
|
20
|
+
|
|
21
|
+
// ── Hardcoded models (parameters from models.dev) ─────────────────────────
|
|
22
|
+
|
|
23
|
+
const MODELS: ProviderModelConfig[] = [
|
|
24
|
+
{ id: "deepseek-v3.2", name: "DeepSeek V3.2", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 163_840, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
25
|
+
{ id: "glm-4.7", name: "GLM 4.7", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
26
|
+
{ id: "glm-5.1", name: "GLM 5.1", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
27
|
+
{ id: "kimi-k2.5", name: "Kimi K2.5", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
28
|
+
{ id: "kimi-k2.6", name: "Kimi K2.6", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
29
|
+
{ id: "minimax-m2.5", name: "MiniMax M2.5", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 204_800, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
30
|
+
{ id: "minimax-m2.7", name: "MiniMax M2.7", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 196_608, maxTokens: 196_608, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
31
|
+
{ id: "doubao-seed-2.0-code", name: "Doubao Seed 2.0 Code", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 256_000, maxTokens: 128_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
32
|
+
{ id: "doubao-seed-2.0-pro", name: "Doubao Seed 2.0 Pro", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 256_000, maxTokens: 128_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
33
|
+
{ id: "doubao-seed-2.0-lite", name: "Doubao Seed 2.0 Lite", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 256_000, maxTokens: 32_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
34
|
+
{ id: "doubao-seed-code", name: "Doubao Seed Code", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 256_000, maxTokens: 16_384, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// ── Entry ──────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
export function setupArkCoding(pi: ExtensionAPI) {
|
|
40
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
41
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
42
|
+
baseUrl: BASE_URL,
|
|
43
|
+
api: "openai-completions",
|
|
44
|
+
authHeader: true,
|
|
45
|
+
models: MODELS,
|
|
46
|
+
oauth: {
|
|
47
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
48
|
+
|
|
49
|
+
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
50
|
+
const apiKey = (await callbacks.onPrompt({
|
|
51
|
+
message: "Enter ARK Coding Plan API key:",
|
|
52
|
+
placeholder: "your-api-key",
|
|
53
|
+
})).trim();
|
|
54
|
+
|
|
55
|
+
if (!apiKey) throw new Error("API key cannot be empty.");
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
refresh: apiKey,
|
|
59
|
+
access: apiKey,
|
|
60
|
+
expires: Date.now() + 1000 * 365.24 * 24 * 3600 * 1000, // ~1000 years
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
refreshToken(cred: OAuthCredentials): Promise<OAuthCredentials> {
|
|
65
|
+
return Promise.resolve(cred); // API key never expires
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
getApiKey(cred: OAuthCredentials): string {
|
|
69
|
+
return cred.access;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { setupArkCoding } from "./ark-coding.js";
|
|
2
|
+
import { setupOllamaCloud } from "./ollama-cloud.js";
|
|
3
|
+
import { setupQianfanCoding } from "./qianfan-coding.js";
|
|
4
|
+
|
|
5
|
+
export function setupProviders(pi: any) {
|
|
6
|
+
setupArkCoding(pi);
|
|
7
|
+
setupOllamaCloud(pi);
|
|
8
|
+
setupQianfanCoding(pi);
|
|
9
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Cloud — OAuth/subscription provider with hardcoded models
|
|
3
|
+
*
|
|
4
|
+
* Provider: "ollama-cloud"
|
|
5
|
+
* Base URL: https://ollama.com/v1 (OpenAI-compatible)
|
|
6
|
+
* Auth: OAuth/subscription login → prompt for API key
|
|
7
|
+
*
|
|
8
|
+
* All models hardcoded from models.dev. No dynamic fetching, no config file caching.
|
|
9
|
+
* - No auth → no models in /model (clean UX, via hasConfiguredAuth)
|
|
10
|
+
* - Login → models become available immediately
|
|
11
|
+
* - Startup → models registered unconditionally (hardcoded)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionAPI, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
|
|
16
|
+
|
|
17
|
+
const PROVIDER_ID = "ollama-cloud";
|
|
18
|
+
const PROVIDER_DISPLAY_NAME = "Ollama Cloud";
|
|
19
|
+
const BASE_URL = "https://ollama.com/v1";
|
|
20
|
+
|
|
21
|
+
// ── Hardcoded models (from models.dev) ────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
const MODELS: ProviderModelConfig[] = [
|
|
24
|
+
{ id: "cogito-2.1:671b", name: "cogito-2.1:671b", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 163_840, maxTokens: 32_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
25
|
+
{ id: "deepseek-v3.1:671b", name: "deepseek-v3.1:671b", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 163_840, maxTokens: 163_840, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
26
|
+
{ id: "deepseek-v3.2", name: "deepseek-v3.2", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 163_840, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
27
|
+
{ id: "deepseek-v4-flash", name: "deepseek-v4-flash", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1_048_576, maxTokens: 1_048_576, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
28
|
+
{ id: "deepseek-v4-pro", name: "deepseek-v4-pro", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1_048_576, maxTokens: 1_048_576, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
29
|
+
{ id: "devstral-2:123b", name: "devstral-2:123b", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
30
|
+
{ id: "devstral-small-2:24b", name: "devstral-small-2:24b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
31
|
+
{ id: "gemini-3-flash-preview", name: "gemini-3-flash-preview", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1_048_576, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
32
|
+
{ id: "gemma3:12b", name: "gemma3:12b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131_072, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
33
|
+
{ id: "gemma3:27b", name: "gemma3:27b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131_072, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
34
|
+
{ id: "gemma3:4b", name: "gemma3:4b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131_072, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
35
|
+
{ id: "gemma4:31b", name: "gemma4:31b", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
36
|
+
{ id: "glm-4.6", name: "glm-4.6", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
37
|
+
{ id: "glm-4.7", name: "glm-4.7", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
38
|
+
{ id: "glm-5", name: "glm-5", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
39
|
+
{ id: "glm-5.1", name: "glm-5.1", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
40
|
+
{ id: "gpt-oss:120b", name: "gpt-oss:120b", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131_072, maxTokens: 32_768, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
41
|
+
{ id: "gpt-oss:20b", name: "gpt-oss:20b", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131_072, maxTokens: 32_768, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
42
|
+
{ id: "kimi-k2-thinking", name: "kimi-k2-thinking", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
43
|
+
{ id: "kimi-k2.5", name: "kimi-k2.5", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
44
|
+
{ id: "kimi-k2.6", name: "kimi-k2.6", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
45
|
+
{ id: "kimi-k2:1t", name: "kimi-k2:1t", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
46
|
+
{ id: "minimax-m2", name: "minimax-m2", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 204_800, maxTokens: 128_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
47
|
+
{ id: "minimax-m2.1", name: "minimax-m2.1", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 204_800, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
48
|
+
{ id: "minimax-m2.5", name: "minimax-m2.5", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 204_800, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
49
|
+
{ id: "minimax-m2.7", name: "minimax-m2.7", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 196_608, maxTokens: 196_608, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
50
|
+
{ id: "ministral-3:14b", name: "ministral-3:14b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 128_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
51
|
+
{ id: "ministral-3:3b", name: "ministral-3:3b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 128_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
52
|
+
{ id: "ministral-3:8b", name: "ministral-3:8b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 128_000, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
53
|
+
{ id: "mistral-large-3:675b", name: "mistral-large-3:675b", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
54
|
+
{ id: "nemotron-3-nano:30b", name: "nemotron-3-nano:30b", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1_048_576, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
55
|
+
{ id: "nemotron-3-super", name: "nemotron-3-super", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
56
|
+
{ id: "qwen3-coder-next", name: "qwen3-coder-next", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
57
|
+
{ id: "qwen3-coder:480b", name: "qwen3-coder:480b", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
58
|
+
{ id: "qwen3-next:80b", name: "qwen3-next:80b", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 32_768, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
59
|
+
{ id: "qwen3-vl:235b", name: "qwen3-vl:235b", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 32_768, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
60
|
+
{ id: "qwen3-vl:235b-instruct", name: "qwen3-vl:235b-instruct", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
61
|
+
{ id: "qwen3.5:397b", name: "qwen3.5:397b", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
62
|
+
{ id: "rnj-1:8b", name: "rnj-1:8b", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 32_768, maxTokens: 4_096, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// ── Entry ──────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
export function setupOllamaCloud(pi: ExtensionAPI) {
|
|
68
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
69
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
70
|
+
baseUrl: BASE_URL,
|
|
71
|
+
api: "openai-completions",
|
|
72
|
+
authHeader: true,
|
|
73
|
+
models: MODELS,
|
|
74
|
+
oauth: {
|
|
75
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
76
|
+
|
|
77
|
+
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
78
|
+
const apiKey = (await callbacks.onPrompt({
|
|
79
|
+
message: "Enter Ollama Cloud API key:",
|
|
80
|
+
placeholder: "your-api-key",
|
|
81
|
+
})).trim();
|
|
82
|
+
|
|
83
|
+
if (!apiKey) throw new Error("API key cannot be empty.");
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
refresh: apiKey,
|
|
87
|
+
access: apiKey,
|
|
88
|
+
expires: Date.now() + 1000 * 365.24 * 24 * 3600 * 1000, // ~1000 years
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
refreshToken(cred: OAuthCredentials): Promise<OAuthCredentials> {
|
|
93
|
+
return Promise.resolve(cred); // API key never expires
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
getApiKey(cred: OAuthCredentials): string {
|
|
97
|
+
return cred.access;
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baidu Qianfan Coding Plan — OAuth/subscription provider with hardcoded models
|
|
3
|
+
*
|
|
4
|
+
* Provider: "qianfan-coding"
|
|
5
|
+
* Base URL: https://qianfan.baidubce.com/v2/coding (OpenAI-compatible)
|
|
6
|
+
* Auth: OAuth/subscription login → prompt for API key
|
|
7
|
+
*
|
|
8
|
+
* All models hardcoded. No dynamic fetching, no config file caching.
|
|
9
|
+
* - No auth → no models in /model (clean UX, via hasConfiguredAuth)
|
|
10
|
+
* - Login → models become available immediately
|
|
11
|
+
* - Startup → models registered unconditionally (hardcoded)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionAPI, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
|
|
16
|
+
|
|
17
|
+
const PROVIDER_ID = "qianfan-coding";
|
|
18
|
+
const PROVIDER_DISPLAY_NAME = "Baidu Qianfan Coding Plan";
|
|
19
|
+
const BASE_URL = "https://qianfan.baidubce.com/v2/coding";
|
|
20
|
+
|
|
21
|
+
// ── Hardcoded models (parameters from models.dev + Baidu docs) ────────────
|
|
22
|
+
|
|
23
|
+
const MODELS: ProviderModelConfig[] = [
|
|
24
|
+
{ id: "deepseek-v3.2", name: "DeepSeek V3.2", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 163_840, maxTokens: 65_536, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
25
|
+
{ id: "glm-4.7", name: "GLM 4.7", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
26
|
+
{ id: "glm-5", name: "GLM 5", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
27
|
+
{ id: "glm-5.1", name: "GLM 5.1", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 202_752, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
28
|
+
{ id: "kimi-k2.5", name: "Kimi K2.5", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 262_144, maxTokens: 262_144, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
29
|
+
{ id: "minimax-m2.1", name: "MiniMax M2.1", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 204_800, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
30
|
+
{ id: "minimax-m2.5", name: "MiniMax M2.5", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 204_800, maxTokens: 131_072, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
31
|
+
{ id: "ernie-4.5-turbo-20260402", name: "ERNIE 4.5 Turbo", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128_000, maxTokens: 12_288, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
32
|
+
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1_048_576, maxTokens: 1_048_576, compat: { supportsDeveloperRole: false, supportsReasoningEffort: true } as any },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// ── Entry ──────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export function setupQianfanCoding(pi: ExtensionAPI) {
|
|
38
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
39
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
40
|
+
baseUrl: BASE_URL,
|
|
41
|
+
api: "openai-completions",
|
|
42
|
+
authHeader: true,
|
|
43
|
+
models: MODELS,
|
|
44
|
+
oauth: {
|
|
45
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
46
|
+
|
|
47
|
+
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
48
|
+
const apiKey = (await callbacks.onPrompt({
|
|
49
|
+
message: "Enter Baidu Qianfan Coding Plan API key:",
|
|
50
|
+
placeholder: "your-api-key",
|
|
51
|
+
})).trim();
|
|
52
|
+
|
|
53
|
+
if (!apiKey) throw new Error("API key cannot be empty.");
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
refresh: apiKey,
|
|
57
|
+
access: apiKey,
|
|
58
|
+
expires: Date.now() + 1000 * 365.24 * 24 * 3600 * 1000, // ~1000 years
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
refreshToken(cred: OAuthCredentials): Promise<OAuthCredentials> {
|
|
63
|
+
return Promise.resolve(cred); // API key never expires
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
getApiKey(cred: OAuthCredentials): string {
|
|
67
|
+
return cred.access;
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
package/extensions/safety.ts
CHANGED
package/extensions/settings.ts
CHANGED
|
@@ -8,12 +8,27 @@ import * as path from "node:path";
|
|
|
8
8
|
import * as os from "node:os";
|
|
9
9
|
import type { Model } from "@earendil-works/pi-ai";
|
|
10
10
|
|
|
11
|
-
const CONFIG_DIR = path.join(os.homedir(), ".pi", "agent"
|
|
11
|
+
const CONFIG_DIR = path.join(os.homedir(), ".pi", "agent");
|
|
12
12
|
const CONFIG_FILE = path.join(CONFIG_DIR, "decorated-pi.json");
|
|
13
13
|
|
|
14
|
+
export interface ProviderModelEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
reasoning: boolean;
|
|
18
|
+
contextWindow: number;
|
|
19
|
+
maxTokens: number;
|
|
20
|
+
input: ("text" | "image")[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProviderCache {
|
|
24
|
+
lastSynced?: string;
|
|
25
|
+
models: ProviderModelEntry[];
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
export interface DecoratedPiConfig {
|
|
15
29
|
imageModelKey?: string | null;
|
|
16
30
|
compactModelKey?: string | null;
|
|
31
|
+
providers?: Record<string, ProviderCache>;
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
export function loadConfig(): DecoratedPiConfig {
|
|
@@ -41,6 +56,28 @@ export function parseModelKey(key: string): { provider: string; modelId: string
|
|
|
41
56
|
return { provider: key.slice(0, i), modelId: key.slice(i + 1) };
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
// ─── Provider ────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
export function loadProvider(name: string): ProviderCache | null {
|
|
62
|
+
return loadConfig().providers?.[name] ?? null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function saveProvider(name: string, data: ProviderCache) {
|
|
66
|
+
if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
67
|
+
const current = loadConfig();
|
|
68
|
+
if (!current.providers) current.providers = {};
|
|
69
|
+
current.providers[name] = data;
|
|
70
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(current, null, 2) + "\n", "utf-8");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function removeProvider(name: string) {
|
|
74
|
+
const current = loadConfig();
|
|
75
|
+
if (current.providers?.[name]) {
|
|
76
|
+
delete current.providers[name];
|
|
77
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(current, null, 2) + "\n", "utf-8");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
44
81
|
// ─── Getter ─────────────────────────────────────────────────────────────────
|
|
45
82
|
|
|
46
83
|
export function getImageModelKey(): string | null {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decorated-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Essential utilities for pi: safety gates, secret redaction, smart @ completion, dynamic AGENTS loading, image fallback, and LSP tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"openai": "^6.37.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
+
"@earendil-works/pi-ai": "*",
|
|
33
34
|
"@earendil-works/pi-coding-agent": "*",
|
|
34
35
|
"@earendil-works/pi-tui": "*",
|
|
35
36
|
"typebox": "*"
|