Skip to content

Commit 49cab44

Browse files
✨ feat: Update models info and add keyStyle for i18n dotted keys (#160)
* feat(i18n): add keyStyle for dotted keys * 💄 style: Update models --------- Co-authored-by: canisminor1990 <i@canisminor.cc>
1 parent b6e4a9f commit 49cab44

File tree

9 files changed

+152
-43
lines changed

9 files changed

+152
-43
lines changed

packages/common/models.ts

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// refs: https://github.com/lobehub/lobe-chat/blob/main/src/config/modelProviders/openai.ts
22
// Auto-generated file. Do not edit manually.
3-
// Last updated: 2025-08-08T14:08:59.877Z
3+
// Last updated: 2025-12-25T04:17:46.914Z
44

55
export enum LanguageModel {
66
/**
@@ -44,7 +44,7 @@ export enum LanguageModel {
4444
*/
4545
GPT_4O_2024_11_20 = 'gpt-4o-2024-11-20',
4646
/**
47-
* GPT-4o Audio
47+
* GPT-4o Audio Preview
4848
*/
4949
GPT_4O_AUDIO_PREVIEW = 'gpt-4o-audio-preview',
5050
/**
@@ -87,10 +87,6 @@ export enum LanguageModel {
8787
* GPT-4.1 nano
8888
*/
8989
GPT_4_1_NANO = 'gpt-4.1-nano',
90-
/**
91-
* GPT-4.5 Preview
92-
*/
93-
GPT_4_5_PREVIEW = 'gpt-4.5-preview',
9490
/**
9591
* GPT-4 Turbo
9692
*/
@@ -107,10 +103,42 @@ export enum LanguageModel {
107103
* GPT-5
108104
*/
109105
GPT_5 = 'gpt-5',
106+
/**
107+
* GPT-5.1
108+
*/
109+
GPT_5_1 = 'gpt-5.1',
110+
/**
111+
* GPT-5.1 Chat
112+
*/
113+
GPT_5_1_CHAT_LATEST = 'gpt-5.1-chat-latest',
114+
/**
115+
* GPT-5.1 Codex
116+
*/
117+
GPT_5_1_CODEX = 'gpt-5.1-codex',
118+
/**
119+
* GPT-5.1 Codex mini
120+
*/
121+
GPT_5_1_CODEX_MINI = 'gpt-5.1-codex-mini',
122+
/**
123+
* GPT-5.2
124+
*/
125+
GPT_5_2 = 'gpt-5.2',
126+
/**
127+
* GPT-5.2 Chat
128+
*/
129+
GPT_5_2_CHAT_LATEST = 'gpt-5.2-chat-latest',
130+
/**
131+
* GPT-5.2 pro
132+
*/
133+
GPT_5_2_PRO = 'gpt-5.2-pro',
110134
/**
111135
* GPT-5 Chat
112136
*/
113137
GPT_5_CHAT_LATEST = 'gpt-5-chat-latest',
138+
/**
139+
* GPT-5 Codex
140+
*/
141+
GPT_5_CODEX = 'gpt-5-codex',
114142
/**
115143
* GPT-5 mini
116144
*/
@@ -120,17 +148,17 @@ export enum LanguageModel {
120148
*/
121149
GPT_5_NANO = 'gpt-5-nano',
122150
/**
123-
* o1
151+
* GPT-5 pro
124152
*/
125-
O1 = 'o1',
153+
GPT_5_PRO = 'gpt-5-pro',
126154
/**
127-
* o1-mini
155+
* GPT Audio
128156
*/
129-
O1_MINI = 'o1-mini',
157+
GPT_AUDIO = 'gpt-audio',
130158
/**
131-
* o1-preview
159+
* o1
132160
*/
133-
O1_PREVIEW = 'o1-preview',
161+
O1 = 'o1',
134162
/**
135163
* o1-pro
136164
*/
@@ -162,6 +190,15 @@ export enum LanguageModel {
162190
}
163191

164192
export const ModelTokens: Record<LanguageModel, number> = {
193+
[LanguageModel.GPT_5_2]: 400_000,
194+
[LanguageModel.GPT_5_2_PRO]: 400_000,
195+
[LanguageModel.GPT_5_2_CHAT_LATEST]: 128_000,
196+
[LanguageModel.GPT_5_1]: 400_000,
197+
[LanguageModel.GPT_5_1_CHAT_LATEST]: 128_000,
198+
[LanguageModel.GPT_5_1_CODEX]: 400_000,
199+
[LanguageModel.GPT_5_1_CODEX_MINI]: 400_000,
200+
[LanguageModel.GPT_5_PRO]: 400_000,
201+
[LanguageModel.GPT_5_CODEX]: 400_000,
165202
[LanguageModel.GPT_5]: 400_000,
166203
[LanguageModel.GPT_5_MINI]: 400_000,
167204
[LanguageModel.GPT_5_NANO]: 400_000,
@@ -173,21 +210,19 @@ export const ModelTokens: Record<LanguageModel, number> = {
173210
[LanguageModel.O3_DEEP_RESEARCH]: 200_000,
174211
[LanguageModel.O3_MINI]: 200_000,
175212
[LanguageModel.O1_PRO]: 200_000,
176-
[LanguageModel.O1_MINI]: 128_000,
177213
[LanguageModel.O1]: 200_000,
178-
[LanguageModel.O1_PREVIEW]: 128_000,
179214
[LanguageModel.GPT_4_1]: 1_047_576,
180215
[LanguageModel.GPT_4_1_MINI]: 1_047_576,
181216
[LanguageModel.GPT_4_1_NANO]: 1_047_576,
182-
[LanguageModel.GPT_4_5_PREVIEW]: 128_000,
183217
[LanguageModel.GPT_4O_MINI]: 128_000,
184218
[LanguageModel.GPT_4O_MINI_SEARCH_PREVIEW]: 128_000,
185-
[LanguageModel.GPT_4O_MINI_AUDIO_PREVIEW]: 128_000,
186219
[LanguageModel.GPT_4O]: 128_000,
187220
[LanguageModel.GPT_4O_SEARCH_PREVIEW]: 128_000,
188221
[LanguageModel.GPT_4O_2024_11_20]: 128_000,
189222
[LanguageModel.GPT_4O_2024_05_13]: 128_000,
223+
[LanguageModel.GPT_AUDIO]: 128_000,
190224
[LanguageModel.GPT_4O_AUDIO_PREVIEW]: 128_000,
225+
[LanguageModel.GPT_4O_MINI_AUDIO_PREVIEW]: 128_000,
191226
[LanguageModel.CHATGPT_4O_LATEST]: 128_000,
192227
[LanguageModel.GPT_4_TURBO]: 128_000,
193228
[LanguageModel.GPT_4_TURBO_2024_04_09]: 128_000,

packages/lobe-i18n/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ This project provides some additional configuration items set with environment v
179179
| modelName | | `string` | `gpt-3.5-turbo` | Model to use |
180180
| output | `*` | `string` | - | Location to store localized files |
181181
| outputLocales | `*` | `string[] ` | `[]` | All the languages to be translated |
182+
| keyStyle | | `string` | `nested` | How to resolve dotted keys: `nested`, `flat`, or `auto` (preserve entry) |
182183
| reference | | `string` | - | Provide some context for more accurate translations |
183184
| saveImmediately | | `boolean` | `false` | Save translation results immediately after each chunk is completed |
184185
| splitToken | | `number` | - | Split the localized JSON file by tokens, automatically calculated by default |

packages/lobe-i18n/README.zh-CN.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,22 @@ $ lobe-i18n -c './custom-config.js' # or use the full flag --config
172172

173173
## 🌏 Locale 配置
174174

175-
| 属性名称 | 必填 | 类型 | 默认值 | 描述 |
176-
| --------------- | ---- | -------------- | --------------- | -------------------------------------------------------- |
177-
| entry | `*` | `string` | - | 入口文件或文件夹 |
178-
| entryLocale | `*` | `string` | - | 作为翻译参考的语言 |
179-
| modelName | | `string` | `gpt-3.5-turbo` | 使用的模型 |
180-
| output | `*` | `string` | - | 存储本地化文件的位置 |
181-
| outputLocales | `*` | `string[] ` | `[]` | 需要进行翻译的所有语言 |
182-
| reference | | `string` | - | 提供一些上下文以获得更准确的翻译 |
183-
| saveImmediately | | `boolean` | `false` | 在每个翻译块完成后立即保存翻译结果 |
184-
| splitToken | | `number` | - | 按令牌分割本地化 JSON 文件,默认自动计算 |
185-
| temperature | | `number` | `0` | 使用的采样温度 |
186-
| topP | | `number` | `1` | 生成过程中的核采样方法概率阈值,取值越大生成的随机性越高 |
187-
| concurrency | | `number` | `5` | 同时并发的队列请求数量 |
188-
| experimental | | `experimental` | `{}` | 实验性功能,见下文 |
189-
| markdown | | `markdown` | `{}` |`markdown` 配置说明 |
175+
| 属性名称 | 必填 | 类型 | 默认值 | 描述 |
176+
| --------------- | ---- | -------------- | --------------- | ------------------------------------------------------------ |
177+
| entry | `*` | `string` | - | 入口文件或文件夹 |
178+
| entryLocale | `*` | `string` | - | 作为翻译参考的语言 |
179+
| modelName | | `string` | `gpt-3.5-turbo` | 使用的模型 |
180+
| output | `*` | `string` | - | 存储本地化文件的位置 |
181+
| outputLocales | `*` | `string[] ` | `[]` | 需要进行翻译的所有语言 |
182+
| keyStyle | | `string` | `nested` | 处理点分隔键的方式:`nested``flat``auto`(按入口文件) |
183+
| reference | | `string` | - | 提供一些上下文以获得更准确的翻译 |
184+
| saveImmediately | | `boolean` | `false` | 在每个翻译块完成后立即保存翻译结果 |
185+
| splitToken | | `number` | - | 按令牌分割本地化 JSON 文件,默认自动计算 |
186+
| temperature | | `number` | `0` | 使用的采样温度 |
187+
| topP | | `number` | `1` | 生成过程中的核采样方法概率阈值,取值越大生成的随机性越高 |
188+
| concurrency | | `number` | `5` | 同时并发的队列请求数量 |
189+
| experimental | | `experimental` | `{}` | 实验性功能,见下文 |
190+
| markdown | | `markdown` | `{}` |`markdown` 配置说明 |
190191

191192
#### `experimental`
192193

packages/lobe-i18n/src/commands/TranslateLocale/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class TranslateLocale {
118118
);
119119
const targetFilename = resolve(config.output, locale, filename);
120120
const entryObj = entry[filename] as LocaleObj;
121-
const targetObj = diff(entryObj, getLocaleObj(targetFilename)).target;
121+
const targetObj = diff(entryObj, getLocaleObj(targetFilename), config).target;
122122
if (isEqualJsonKeys(entryObj, targetObj)) continue;
123123

124124
this.query.push({
@@ -149,7 +149,7 @@ class TranslateLocale {
149149
for (const locale of config.outputLocales) {
150150
const targetFilename = resolve(config.output, locale) + '.json';
151151
const entryObj = entry;
152-
const targetObj = diff(entryObj, getLocaleObj(targetFilename)).target;
152+
const targetObj = diff(entryObj, getLocaleObj(targetFilename), config).target;
153153
if (isEqualJsonKeys(entryObj, targetObj)) continue;
154154

155155
this.query.push({

packages/lobe-i18n/src/types/config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { LanguageModel } from '../../../common/models';
22

3+
export type KeyStyle = 'auto' | 'flat' | 'nested';
4+
35
export interface I18nConfigLocale {
46
/**
57
* @description Number of concurrently pending promises returned
@@ -13,6 +15,11 @@ export interface I18nConfigLocale {
1315
* @description The language that will use as translation ref
1416
*/
1517
entryLocale: string;
18+
/**
19+
* @description How to resolve dot-delimited keys when building diff entries
20+
* @default 'nested'
21+
*/
22+
keyStyle?: KeyStyle;
1623
/**
1724
* @description ChatGPT model name to use
1825
*/

packages/lobe-i18n/src/utils/diffJson.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,46 @@ describe('diffJson', () => {
7979
});
8080
});
8181

82+
it('should flatten nested paths when keyStyle is flat', () => {
83+
const entry = {
84+
a: {
85+
bc: {
86+
b: 'value',
87+
},
88+
},
89+
};
90+
const target = {
91+
a: {
92+
bc: {},
93+
},
94+
};
95+
96+
const result = diff(entry, target, { keyStyle: 'flat' });
97+
98+
expect(result.entry).toEqual({
99+
'a.bc.b': 'value',
100+
});
101+
});
102+
103+
it('should preserve mixed keys when keyStyle is auto', () => {
104+
const entry = {
105+
'a.bc.b': 'value',
106+
'nested': {
107+
key: 'value2',
108+
},
109+
};
110+
const target = {};
111+
112+
const result = diff(entry, target, { keyStyle: 'auto' });
113+
114+
expect(result.entry).toEqual({
115+
'a.bc.b': 'value',
116+
'nested': {
117+
key: 'value2',
118+
},
119+
});
120+
});
121+
82122
it('should handle mixed add and remove operations', () => {
83123
const entry = {
84124
common: 'value',

packages/lobe-i18n/src/utils/diffJson.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,50 @@ import { diff as justDiff } from 'just-diff';
22
import { cloneDeep, set, unset } from 'lodash-es';
33

44
import { LocaleObj } from '@/types';
5+
import { I18nConfig, KeyStyle } from '@/types/config';
56

6-
export const diff = (entry: LocaleObj, target: LocaleObj) => {
7+
type DiffPath = string | Array<number | string>;
8+
9+
const hasOwnKey = (obj: LocaleObj, key: string) => Object.prototype.hasOwnProperty.call(obj, key);
10+
11+
const resolveDiffPath = (source: LocaleObj, path: DiffPath, keyStyle: KeyStyle): DiffPath => {
12+
if (Array.isArray(path)) {
13+
if (keyStyle === 'flat') {
14+
return [path.join('.')];
15+
}
16+
if (keyStyle === 'auto') {
17+
const joinedPath = path.join('.');
18+
if (hasOwnKey(source, joinedPath)) return [joinedPath];
19+
}
20+
return path;
21+
}
22+
23+
if (keyStyle === 'flat') return [path];
24+
if (keyStyle === 'auto' && hasOwnKey(source, path)) return [path];
25+
return path;
26+
};
27+
28+
export const diff = (
29+
entry: LocaleObj,
30+
target: LocaleObj,
31+
config?: Pick<I18nConfig, 'keyStyle'>,
32+
) => {
33+
const keyStyle = config?.keyStyle ?? 'nested';
734
const diffResult = justDiff(target, entry);
835
const add = diffResult.filter((item) => item.op === 'add');
936
const remove = diffResult.filter((item) => item.op === 'remove');
1037

1138
const cloneTarget = cloneDeep(target);
12-
const extra = {};
39+
const extra: LocaleObj = {};
1340

1441
for (const item of remove) {
15-
unset(cloneTarget, item.path);
42+
const path = resolveDiffPath(target, item.path as DiffPath, keyStyle);
43+
unset(cloneTarget, path);
1644
}
1745

1846
for (const item of add) {
19-
set(extra, item.path, item.value);
47+
const path = resolveDiffPath(entry, item.path as DiffPath, keyStyle);
48+
set(extra, path, item.value);
2049
}
2150

2251
return {

packages/lobe-i18n/src/utils/splitJsonToChunks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const splitJsonToChunks = (
5252
target: LocaleObj,
5353
prompt: string,
5454
): LocaleObj[] => {
55-
const extraJSON = diff(entry, target).entry;
55+
const extraJSON = diff(entry, target, config).entry;
5656
const splitToken = getSplitToken(config, prompt);
5757
const splitObj = splitJSONtoSmallChunks(extraJSON, splitToken);
5858
return splitObj;

scripts/update-models.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface AIChatModelCard {
1717
}
1818

1919
const LOBE_CHAT_CONFIG_URL =
20-
'https://raw.githubusercontent.com/lobehub/lobe-chat/refs/heads/main/src/config/aiModels/openai.ts';
20+
'https://raw.githubusercontent.com/lobehub/lobe-chat/refs/heads/next/packages/model-bank/src/aiModels/openai.ts';
2121
const MODELS_FILE_PATH = path.join(__dirname, '../packages/common/models.ts');
2222

2323
/**
@@ -119,10 +119,6 @@ function parseOpenAIConfig(content: string): AIChatModelCard[] {
119119
const enabledMatch = modelStr.match(/enabled:\s*(true|false)/);
120120
const enabled = enabledMatch ? enabledMatch[1] === 'true' : true;
121121

122-
// 检查是否为 chat 类型
123-
const typeMatch = modelStr.match(/type:\s*["'`]([^"'`]+)["'`]/);
124-
if (!typeMatch || typeMatch[1] !== 'chat') continue;
125-
126122
// 确保必需的字段不为 undefined
127123
if (!displayName || !id) continue;
128124

0 commit comments

Comments
 (0)