pi-cny-cost 0.1.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/LICENSE +21 -0
- package/README.md +112 -0
- package/README_zh.md +112 -0
- package/dist/builtins.d.ts +35 -0
- package/dist/builtins.js +146 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +58 -0
- package/dist/prices.d.ts +33 -0
- package/dist/prices.js +97 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jiajun Chen <tychenjiajun@live.cn>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# pi-cny-cost
|
|
2
|
+
|
|
3
|
+
A [Pi](https://github.com/earendil-works/pi) extension that displays model token costs in **CNY (Chinese Yuan)** instead of USD.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Adds a `cny-cost` command to toggle a footer showing input/output token counts and total cost in ¥
|
|
8
|
+
- Built-in CNY prices for:
|
|
9
|
+
- **DeepSeek** — deepseek-v4-flash, deepseek-v4-pro
|
|
10
|
+
- **MiniMax** — MiniMax-M2, M2.1, M2.5, M2.7 and highspeed variants
|
|
11
|
+
- **Xiaomi MiMo** — mimo-v2.5-pro, mimo-v2-pro, mimo-v2.5, mimo-v2-omni, mimo-v2-flash
|
|
12
|
+
- Customizable via `~/.pi/agent/cny.json`:
|
|
13
|
+
- Override the USD→CNY exchange rate (default: 7.25)
|
|
14
|
+
- Add or override per-model CNY prices
|
|
15
|
+
- Set rate to `0` to disable USD conversion entirely
|
|
16
|
+
- Falls back to USD cost × exchange rate for models without built-in CNY prices
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# From the project directory
|
|
22
|
+
pnpm build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then add the extension to your Pi config (e.g. `~/.pi/agent/config.json`):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"extensions": ["path/to/pi-cny-cost"]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or if you have it in your project's `package.json`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"pi": {
|
|
38
|
+
"extensions": ["./src/index.ts"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
In the Pi TUI, run:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
/cny-cost
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This toggles a footer bar showing:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
↑12.3k ↓4.5k ¥1.23 model-name
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Where ↑ = input tokens, ↓ = output tokens, and ¥ = total CNY cost.
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
Create or edit `~/.pi/agent/cny.json`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"rate": 7.25,
|
|
66
|
+
"providers": {
|
|
67
|
+
"openai": {
|
|
68
|
+
"modelOverrides": {
|
|
69
|
+
"gpt-4o": {
|
|
70
|
+
"cnyCost": {
|
|
71
|
+
"input": 18.125,
|
|
72
|
+
"output": 72.5,
|
|
73
|
+
"cacheRead": 3.625,
|
|
74
|
+
"cacheWrite": 18.125
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"anthropic": {
|
|
80
|
+
"models": [
|
|
81
|
+
{
|
|
82
|
+
"id": "claude-sonnet-4-20250514",
|
|
83
|
+
"cnyCost": {
|
|
84
|
+
"input": 22.5,
|
|
85
|
+
"output": 112.5,
|
|
86
|
+
"cacheRead": 2.25,
|
|
87
|
+
"cacheWrite": 22.5
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Config options
|
|
97
|
+
|
|
98
|
+
| Field | Type | Default | Description |
|
|
99
|
+
|-------|------|---------|-------------|
|
|
100
|
+
| `rate` | number | `7.25` | USD→CNY exchange rate for automatic conversion. Set to `0` to disable. |
|
|
101
|
+
| `providers` | object | `{}` | Per-provider model overrides. |
|
|
102
|
+
|
|
103
|
+
### Resolution priority
|
|
104
|
+
|
|
105
|
+
1. `cny.json` explicit `cnyCost` (per provider, matching `provider:modelId`)
|
|
106
|
+
2. Built-in CNY prices (exact values from vendor, scoped to provider)
|
|
107
|
+
3. Model's USD cost × exchange rate (skipped if rate is `0`)
|
|
108
|
+
4. No cost shown
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
[MIT](./LICENSE)
|
package/README_zh.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# pi-cny-cost
|
|
2
|
+
|
|
3
|
+
一个 [Pi](https://github.com/earendil-works/pi) 扩展,将模型的 token 费用从美元转换为**人民币 (CNY)** 显示。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- 提供 `cny-cost` 命令,切换底栏显示:输入/输出 token 数 + 总费用(¥)
|
|
8
|
+
- 内置以下模型的人民币定价:
|
|
9
|
+
- **DeepSeek** — deepseek-v4-flash、deepseek-v4-pro
|
|
10
|
+
- **MiniMax** — MiniMax-M2、M2.1、M2.5、M2.7 及高速版本
|
|
11
|
+
- **小米 MiMo** — mimo-v2.5-pro、mimo-v2-pro、mimo-v2.5、mimo-v2-omni、mimo-v2-flash
|
|
12
|
+
- 通过 `~/.pi/agent/cny.json` 自定义:
|
|
13
|
+
- 覆盖美元→人民币汇率(默认:7.25)
|
|
14
|
+
- 添加或覆盖单个模型的人民币价格
|
|
15
|
+
- 设置汇率为 `0` 可完全禁用美元自动换算
|
|
16
|
+
- 没有内置人民币价格的模型,自动使用美元价格 × 汇率作为后备
|
|
17
|
+
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 在项目目录下
|
|
22
|
+
pnpm build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
然后将扩展添加到 Pi 配置(如 `~/.pi/agent/config.json`):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"extensions": ["path/to/pi-cny-cost"]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
或在项目的 `package.json` 中:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"pi": {
|
|
38
|
+
"extensions": ["./src/index.ts"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 使用
|
|
44
|
+
|
|
45
|
+
在 Pi TUI 中运行:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
/cny-cost
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
将切换显示底栏:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
↑12.3k ↓4.5k ¥1.23 model-name
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
其中 ↑ = 输入 token 数,↓ = 输出 token 数,¥ = 总人民币费用。
|
|
58
|
+
|
|
59
|
+
## 配置
|
|
60
|
+
|
|
61
|
+
创建或编辑 `~/.pi/agent/cny.json`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"rate": 7.25,
|
|
66
|
+
"providers": {
|
|
67
|
+
"openai": {
|
|
68
|
+
"modelOverrides": {
|
|
69
|
+
"gpt-4o": {
|
|
70
|
+
"cnyCost": {
|
|
71
|
+
"input": 18.125,
|
|
72
|
+
"output": 72.5,
|
|
73
|
+
"cacheRead": 3.625,
|
|
74
|
+
"cacheWrite": 18.125
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"anthropic": {
|
|
80
|
+
"models": [
|
|
81
|
+
{
|
|
82
|
+
"id": "claude-sonnet-4-20250514",
|
|
83
|
+
"cnyCost": {
|
|
84
|
+
"input": 22.5,
|
|
85
|
+
"output": 112.5,
|
|
86
|
+
"cacheRead": 2.25,
|
|
87
|
+
"cacheWrite": 22.5
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 配置项
|
|
97
|
+
|
|
98
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
99
|
+
|------|------|--------|------|
|
|
100
|
+
| `rate` | number | `7.25` | 美元→人民币汇率,用于自动换算。设为 `0` 可禁用。 |
|
|
101
|
+
| `providers` | object | `{}` | 按提供商配置模型覆盖。 |
|
|
102
|
+
|
|
103
|
+
### 价格解析优先级
|
|
104
|
+
|
|
105
|
+
1. `cny.json` 中显式的 `cnyCost`(按提供商匹配 `provider:modelId`)
|
|
106
|
+
2. 内置人民币价格(来自厂商的精确值,按提供商匹配)
|
|
107
|
+
3. 模型的美元价格 × 汇率(汇率为 `0` 时跳过)
|
|
108
|
+
4. 不显示费用
|
|
109
|
+
|
|
110
|
+
## 许可证
|
|
111
|
+
|
|
112
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CnyCost } from "./types.js";
|
|
2
|
+
export interface ModelCnyPrice {
|
|
3
|
+
provider: string;
|
|
4
|
+
id: string;
|
|
5
|
+
cnyCost: CnyCost;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Built-in CNY prices for DeepSeek models.
|
|
9
|
+
*
|
|
10
|
+
* Source: https://api-docs.deepseek.com/zh-cn/quick_start/pricing
|
|
11
|
+
* Prices in CNY per million tokens.
|
|
12
|
+
*
|
|
13
|
+
* DeepSeek-V4-Pro shows 2.5x discounted prices in parentheses;
|
|
14
|
+
* we use the discounted prices here.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DEEPSEEK_CNY_PRICES: ModelCnyPrice[];
|
|
17
|
+
/**
|
|
18
|
+
* Built-in CNY prices for MiniMax models.
|
|
19
|
+
*
|
|
20
|
+
* Source: MiniMax CN pricing page.
|
|
21
|
+
* Prices in CNY per million tokens.
|
|
22
|
+
*/
|
|
23
|
+
export declare const MINIMAX_CNY_PRICES: ModelCnyPrice[];
|
|
24
|
+
/**
|
|
25
|
+
* Built-in CNY prices for Xiaomi MiMo models (xiaomi provider, not token plan).
|
|
26
|
+
*
|
|
27
|
+
* Source: Xiaomi MiMo CN pricing page.
|
|
28
|
+
* Prices in CNY per million tokens.
|
|
29
|
+
*
|
|
30
|
+
* Tiered pricing by input length (≤256K / 256K-1M).
|
|
31
|
+
* Built-in uses the ≤256K tier. For 256K-1M, use cny.json overrides.
|
|
32
|
+
*/
|
|
33
|
+
export declare const XIAOMI_MIMO_CNY_PRICES: ModelCnyPrice[];
|
|
34
|
+
/** All built-in price lists. Add new providers here. */
|
|
35
|
+
export declare const ALL_BUILTIN_PRICES: ModelCnyPrice[];
|
package/dist/builtins.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in CNY prices for DeepSeek models.
|
|
3
|
+
*
|
|
4
|
+
* Source: https://api-docs.deepseek.com/zh-cn/quick_start/pricing
|
|
5
|
+
* Prices in CNY per million tokens.
|
|
6
|
+
*
|
|
7
|
+
* DeepSeek-V4-Pro shows 2.5x discounted prices in parentheses;
|
|
8
|
+
* we use the discounted prices here.
|
|
9
|
+
*/
|
|
10
|
+
export const DEEPSEEK_CNY_PRICES = [
|
|
11
|
+
{
|
|
12
|
+
provider: "deepseek",
|
|
13
|
+
id: "deepseek-v4-flash",
|
|
14
|
+
cnyCost: {
|
|
15
|
+
input: 1, // cache miss: 1 yuan/million tokens
|
|
16
|
+
output: 2, // 2 yuan/million tokens
|
|
17
|
+
cacheRead: 0.02, // cache hit: 0.02 yuan/million tokens
|
|
18
|
+
cacheWrite: 0,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
provider: "deepseek",
|
|
23
|
+
id: "deepseek-v4-pro",
|
|
24
|
+
cnyCost: {
|
|
25
|
+
input: 3, // cache miss: 3 yuan/million tokens (discounted from 12)
|
|
26
|
+
output: 6, // 6 yuan/million tokens (discounted from 24)
|
|
27
|
+
cacheRead: 0.025, // cache hit: 0.025 yuan/million tokens (discounted from 0.1)
|
|
28
|
+
cacheWrite: 0,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Built-in CNY prices for MiniMax models.
|
|
34
|
+
*
|
|
35
|
+
* Source: MiniMax CN pricing page.
|
|
36
|
+
* Prices in CNY per million tokens.
|
|
37
|
+
*/
|
|
38
|
+
export const MINIMAX_CNY_PRICES = [
|
|
39
|
+
{
|
|
40
|
+
provider: "minimax-cn",
|
|
41
|
+
id: "MiniMax-M2.7",
|
|
42
|
+
cnyCost: { input: 2.1, output: 8.4, cacheRead: 0.42, cacheWrite: 2.625 },
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
provider: "minimax-cn",
|
|
46
|
+
id: "MiniMax-M2.7-highspeed",
|
|
47
|
+
cnyCost: { input: 4.2, output: 16.8, cacheRead: 0.42, cacheWrite: 2.625 },
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
provider: "minimax-cn",
|
|
51
|
+
id: "MiniMax-M2.5",
|
|
52
|
+
cnyCost: { input: 2.1, output: 8.4, cacheRead: 0.21, cacheWrite: 2.625 },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
provider: "minimax-cn",
|
|
56
|
+
id: "MiniMax-M2.5-highspeed",
|
|
57
|
+
cnyCost: { input: 4.2, output: 16.8, cacheRead: 0.21, cacheWrite: 2.625 },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
provider: "minimax-cn",
|
|
61
|
+
id: "M2-her",
|
|
62
|
+
cnyCost: { input: 2.1, output: 8.4, cacheRead: 0, cacheWrite: 0 },
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
provider: "minimax-cn",
|
|
66
|
+
id: "MiniMax-M2.1",
|
|
67
|
+
cnyCost: { input: 2.1, output: 8.4, cacheRead: 0.21, cacheWrite: 2.625 },
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
provider: "minimax-cn",
|
|
71
|
+
id: "MiniMax-M2.1-highspeed",
|
|
72
|
+
cnyCost: { input: 4.2, output: 16.8, cacheRead: 0.21, cacheWrite: 2.625 },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
provider: "minimax-cn",
|
|
76
|
+
id: "MiniMax-M2",
|
|
77
|
+
cnyCost: { input: 2.1, output: 8.4, cacheRead: 0.21, cacheWrite: 2.625 },
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* Built-in CNY prices for Xiaomi MiMo models (xiaomi provider, not token plan).
|
|
82
|
+
*
|
|
83
|
+
* Source: Xiaomi MiMo CN pricing page.
|
|
84
|
+
* Prices in CNY per million tokens.
|
|
85
|
+
*
|
|
86
|
+
* Tiered pricing by input length (≤256K / 256K-1M).
|
|
87
|
+
* Built-in uses the ≤256K tier. For 256K-1M, use cny.json overrides.
|
|
88
|
+
*/
|
|
89
|
+
export const XIAOMI_MIMO_CNY_PRICES = [
|
|
90
|
+
{
|
|
91
|
+
provider: "xiaomi",
|
|
92
|
+
id: "mimo-v2.5-pro",
|
|
93
|
+
cnyCost: {
|
|
94
|
+
input: 7, // ≤256K: 7 (256K-1M: 14)
|
|
95
|
+
output: 21, // ≤256K: 21 (256K-1M: 42)
|
|
96
|
+
cacheRead: 1.4, // ≤256K: 1.4 (256K-1M: 2.8)
|
|
97
|
+
cacheWrite: 0,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
provider: "xiaomi",
|
|
102
|
+
id: "mimo-v2-pro",
|
|
103
|
+
cnyCost: {
|
|
104
|
+
input: 7, // ≤256K: 7 (256K-1M: 14)
|
|
105
|
+
output: 21, // ≤256K: 21 (256K-1M: 42)
|
|
106
|
+
cacheRead: 1.4, // ≤256K: 1.4 (256K-1M: 2.8)
|
|
107
|
+
cacheWrite: 0,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
provider: "xiaomi",
|
|
112
|
+
id: "mimo-v2.5",
|
|
113
|
+
cnyCost: {
|
|
114
|
+
input: 2.8, // ≤256K: 2.8 (256K-1M: 5.6)
|
|
115
|
+
output: 14, // ≤256K: 14 (256K-1M: 28)
|
|
116
|
+
cacheRead: 0.56, // ≤256K: 0.56 (256K-1M: 1.12)
|
|
117
|
+
cacheWrite: 0,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
provider: "xiaomi",
|
|
122
|
+
id: "mimo-v2-omni",
|
|
123
|
+
cnyCost: {
|
|
124
|
+
input: 2.8,
|
|
125
|
+
output: 14,
|
|
126
|
+
cacheRead: 0.56,
|
|
127
|
+
cacheWrite: 0,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
provider: "xiaomi",
|
|
132
|
+
id: "mimo-v2-flash",
|
|
133
|
+
cnyCost: {
|
|
134
|
+
input: 0.7,
|
|
135
|
+
output: 2.1,
|
|
136
|
+
cacheRead: 0.07,
|
|
137
|
+
cacheWrite: 0,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
/** All built-in price lists. Add new providers here. */
|
|
142
|
+
export const ALL_BUILTIN_PRICES = [
|
|
143
|
+
...DEEPSEEK_CNY_PRICES,
|
|
144
|
+
...MINIMAX_CNY_PRICES,
|
|
145
|
+
...XIAOMI_MIMO_CNY_PRICES,
|
|
146
|
+
];
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export type { CnyCost, CnyModel, CnyJsonConfig } from "./types.js";
|
|
3
|
+
export type { ModelCnyPrice } from "./builtins.js";
|
|
4
|
+
export { CNY_PRICE_MAP } from "./prices.js";
|
|
5
|
+
export { ALL_BUILTIN_PRICES, DEEPSEEK_CNY_PRICES, MINIMAX_CNY_PRICES, XIAOMI_MIMO_CNY_PRICES } from "./builtins.js";
|
|
6
|
+
export default function (pi: ExtensionAPI): void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { loadCnyJson, loadCnyJsonCosts, loadRate, resolveCnyCost } from "./prices.js";
|
|
3
|
+
export { CNY_PRICE_MAP } from "./prices.js";
|
|
4
|
+
export { ALL_BUILTIN_PRICES, DEEPSEEK_CNY_PRICES, MINIMAX_CNY_PRICES, XIAOMI_MIMO_CNY_PRICES } from "./builtins.js";
|
|
5
|
+
function formatCost(cny) {
|
|
6
|
+
if (cny < 0.01)
|
|
7
|
+
return cny.toFixed(4);
|
|
8
|
+
if (cny < 1)
|
|
9
|
+
return cny.toFixed(3);
|
|
10
|
+
return cny.toFixed(2);
|
|
11
|
+
}
|
|
12
|
+
export default function (pi) {
|
|
13
|
+
const cnyJson = loadCnyJson();
|
|
14
|
+
const rate = loadRate(cnyJson);
|
|
15
|
+
const cnyJsonCosts = loadCnyJsonCosts(cnyJson);
|
|
16
|
+
pi.registerCommand("cny-cost", {
|
|
17
|
+
description: "Toggle CNY cost display in the footer",
|
|
18
|
+
handler: async (_args, ctx) => {
|
|
19
|
+
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
20
|
+
return {
|
|
21
|
+
dispose: () => { },
|
|
22
|
+
invalidate() { },
|
|
23
|
+
render(width) {
|
|
24
|
+
const model = ctx.model;
|
|
25
|
+
const cnyCost = model
|
|
26
|
+
? resolveCnyCost(model.provider, model.id, model.cost, cnyJsonCosts, rate)
|
|
27
|
+
: undefined;
|
|
28
|
+
let inputTokens = 0;
|
|
29
|
+
let outputTokens = 0;
|
|
30
|
+
let totalCNY = 0;
|
|
31
|
+
for (const e of ctx.sessionManager.getBranch()) {
|
|
32
|
+
if (e.type === "message" && e.message.role === "assistant") {
|
|
33
|
+
const m = e.message;
|
|
34
|
+
inputTokens += m.usage.input;
|
|
35
|
+
outputTokens += m.usage.output;
|
|
36
|
+
if (cnyCost) {
|
|
37
|
+
const cacheRead = m.usage.cacheRead ?? 0;
|
|
38
|
+
const directInput = m.usage.input - cacheRead;
|
|
39
|
+
totalCNY +=
|
|
40
|
+
(directInput / 1_000_000) * cnyCost.input +
|
|
41
|
+
(cacheRead / 1_000_000) * cnyCost.cacheRead +
|
|
42
|
+
(m.usage.output / 1_000_000) * cnyCost.output;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const fmt = (n) => n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`;
|
|
47
|
+
const costStr = cnyCost ? ` ¥${formatCost(totalCNY)}` : "";
|
|
48
|
+
const left = theme.fg("dim", `↑${fmt(inputTokens)} ↓${fmt(outputTokens)}${costStr}`);
|
|
49
|
+
const right = theme.fg("dim", `${model?.id ?? "no-model"}`);
|
|
50
|
+
const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
|
51
|
+
return [truncateToWidth(left + pad + right, width)];
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
ctx.ui.notify("CNY cost footer enabled", "info");
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
package/dist/prices.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CnyCost, CnyJsonConfig } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* All built-in CNY prices, keyed by "provider:modelId".
|
|
4
|
+
*/
|
|
5
|
+
export declare const CNY_PRICE_MAP: Record<string, CnyCost>;
|
|
6
|
+
/**
|
|
7
|
+
* Load and parse ~/.pi/agent/cny.json.
|
|
8
|
+
* Returns undefined if the file doesn't exist or is invalid.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadCnyJson(): CnyJsonConfig | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Extract the exchange rate from cny.json.
|
|
13
|
+
* Defaults to 7.25 if not set. Returns 0 if explicitly disabled.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadRate(config: CnyJsonConfig | undefined): number;
|
|
16
|
+
/**
|
|
17
|
+
* Build a lookup of CNY costs from cny.json, keyed by "provider:modelId".
|
|
18
|
+
* Supports both modelOverrides and inline models arrays.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadCnyJsonCosts(config: CnyJsonConfig | undefined): Record<string, CnyCost>;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve CNY cost for a model in priority order:
|
|
23
|
+
* 1. cny.json explicit cnyCost (per provider, matching provider:modelId)
|
|
24
|
+
* 2. Built-in CNY prices (exact CNY values from vendor, scoped to provider)
|
|
25
|
+
* 3. Model's USD cost converted via rate (skipped if rate is 0)
|
|
26
|
+
* 4. Do nothing (returns undefined)
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCnyCost(provider: string, modelId: string, modelCost: {
|
|
29
|
+
input: number;
|
|
30
|
+
output: number;
|
|
31
|
+
cacheRead: number;
|
|
32
|
+
cacheWrite: number;
|
|
33
|
+
} | undefined, cnyJsonCosts: Record<string, CnyCost>, rate: number): CnyCost | undefined;
|
package/dist/prices.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { ALL_BUILTIN_PRICES } from "./builtins.js";
|
|
5
|
+
const DEFAULT_RATE = 7.25;
|
|
6
|
+
/** Key by "provider:modelId" for exact provider scoping. */
|
|
7
|
+
function priceKey(provider, id) {
|
|
8
|
+
return `${provider}:${id}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* All built-in CNY prices, keyed by "provider:modelId".
|
|
12
|
+
*/
|
|
13
|
+
export const CNY_PRICE_MAP = Object.fromEntries(ALL_BUILTIN_PRICES.map((p) => [priceKey(p.provider, p.id), p.cnyCost]));
|
|
14
|
+
/** Strip // comments and trailing commas from JSON, leaving string literals untouched. */
|
|
15
|
+
function stripJsonComments(input) {
|
|
16
|
+
return input
|
|
17
|
+
.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (m) => (m[0] === '"' ? m : ""))
|
|
18
|
+
.replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (m, tail) => tail ?? (m[0] === '"' ? m : ""));
|
|
19
|
+
}
|
|
20
|
+
function readJsonFile(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
23
|
+
return JSON.parse(stripJsonComments(raw));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load and parse ~/.pi/agent/cny.json.
|
|
31
|
+
* Returns undefined if the file doesn't exist or is invalid.
|
|
32
|
+
*/
|
|
33
|
+
export function loadCnyJson() {
|
|
34
|
+
const cnyPath = join(homedir(), ".pi", "agent", "cny.json");
|
|
35
|
+
return readJsonFile(cnyPath);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract the exchange rate from cny.json.
|
|
39
|
+
* Defaults to 7.25 if not set. Returns 0 if explicitly disabled.
|
|
40
|
+
*/
|
|
41
|
+
export function loadRate(config) {
|
|
42
|
+
if (config?.rate !== undefined)
|
|
43
|
+
return config.rate;
|
|
44
|
+
return DEFAULT_RATE;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build a lookup of CNY costs from cny.json, keyed by "provider:modelId".
|
|
48
|
+
* Supports both modelOverrides and inline models arrays.
|
|
49
|
+
*/
|
|
50
|
+
export function loadCnyJsonCosts(config) {
|
|
51
|
+
if (!config?.providers)
|
|
52
|
+
return {};
|
|
53
|
+
const result = {};
|
|
54
|
+
for (const [providerName, provider] of Object.entries(config.providers)) {
|
|
55
|
+
// modelOverrides
|
|
56
|
+
for (const [modelId, override] of Object.entries(provider.modelOverrides ?? {})) {
|
|
57
|
+
result[priceKey(providerName, modelId)] = override.cnyCost;
|
|
58
|
+
}
|
|
59
|
+
// inline models
|
|
60
|
+
for (const model of provider.models ?? []) {
|
|
61
|
+
result[priceKey(providerName, model.id)] = model.cnyCost;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function convertCost(usdCost, rate) {
|
|
67
|
+
return {
|
|
68
|
+
input: usdCost.input * rate,
|
|
69
|
+
output: usdCost.output * rate,
|
|
70
|
+
cacheRead: usdCost.cacheRead * rate,
|
|
71
|
+
cacheWrite: usdCost.cacheWrite * rate,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve CNY cost for a model in priority order:
|
|
76
|
+
* 1. cny.json explicit cnyCost (per provider, matching provider:modelId)
|
|
77
|
+
* 2. Built-in CNY prices (exact CNY values from vendor, scoped to provider)
|
|
78
|
+
* 3. Model's USD cost converted via rate (skipped if rate is 0)
|
|
79
|
+
* 4. Do nothing (returns undefined)
|
|
80
|
+
*/
|
|
81
|
+
export function resolveCnyCost(provider, modelId, modelCost, cnyJsonCosts, rate) {
|
|
82
|
+
const key = priceKey(provider, modelId);
|
|
83
|
+
// 1. cny.json
|
|
84
|
+
const fromCnyJson = cnyJsonCosts[key];
|
|
85
|
+
if (fromCnyJson)
|
|
86
|
+
return fromCnyJson;
|
|
87
|
+
// 2. built-in
|
|
88
|
+
const builtIn = CNY_PRICE_MAP[key];
|
|
89
|
+
if (builtIn)
|
|
90
|
+
return builtIn;
|
|
91
|
+
// 3. convert (rate > 0)
|
|
92
|
+
if (rate > 0 && modelCost) {
|
|
93
|
+
return convertCost(modelCost, rate);
|
|
94
|
+
}
|
|
95
|
+
// 4. do nothing
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
/**
|
|
3
|
+
* Cost breakdown in CNY (Chinese Yuan) per million tokens.
|
|
4
|
+
*/
|
|
5
|
+
export interface CnyCost {
|
|
6
|
+
/** Input cost (cache miss) in CNY per million tokens */
|
|
7
|
+
input: number;
|
|
8
|
+
/** Output cost in CNY per million tokens */
|
|
9
|
+
output: number;
|
|
10
|
+
/** Cache read cost in CNY per million tokens */
|
|
11
|
+
cacheRead: number;
|
|
12
|
+
/** Cache write cost in CNY per million tokens */
|
|
13
|
+
cacheWrite: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Extended Model interface that includes CNY cost information.
|
|
17
|
+
*/
|
|
18
|
+
export interface CnyModel<TApi extends Api> extends Model<TApi> {
|
|
19
|
+
cnyCost: CnyCost;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Per-model CNY cost override (mirrors models.json ModelOverride shape).
|
|
23
|
+
*/
|
|
24
|
+
export interface CnyModelOverride {
|
|
25
|
+
cnyCost: CnyCost;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Per-provider CNY config (mirrors models.json ProviderConfig shape).
|
|
29
|
+
*/
|
|
30
|
+
export interface CnyProviderConfig {
|
|
31
|
+
modelOverrides?: Record<string, CnyModelOverride>;
|
|
32
|
+
models?: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
cnyCost: CnyCost;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Shape of ~/.pi/agent/cny.json (mirrors models.json layout).
|
|
39
|
+
*/
|
|
40
|
+
export interface CnyJsonConfig {
|
|
41
|
+
rate?: number;
|
|
42
|
+
providers?: Record<string, CnyProviderConfig>;
|
|
43
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-cny-cost",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pi extension to display model costs in CNY (Chinese Yuan)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi",
|
|
7
|
+
"pi-extension",
|
|
8
|
+
"cny",
|
|
9
|
+
"yuan",
|
|
10
|
+
"cost",
|
|
11
|
+
"pricing",
|
|
12
|
+
"deepseek",
|
|
13
|
+
"minimax",
|
|
14
|
+
"xiaomi",
|
|
15
|
+
"mimo"
|
|
16
|
+
],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md",
|
|
29
|
+
"README_zh.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"check": "tsc --noEmit",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"pi": {
|
|
41
|
+
"extensions": ["./src/index.ts"]
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
45
|
+
"@earendil-works/pi-ai": "*",
|
|
46
|
+
"@earendil-works/pi-tui": "*",
|
|
47
|
+
"typebox": "*"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"typescript": "^6.0.3"
|
|
51
|
+
},
|
|
52
|
+
"author": "Jiajun Chen <tychenjiajun@live.cn>",
|
|
53
|
+
"license": "MIT"
|
|
54
|
+
}
|