cortico-world-simple-trpg-check 0.2.2 → 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/LICENSE +20 -20
- package/README.md +18 -6
- package/dist/console.js +52 -7
- package/package.json +54 -46
- package/src/ENV_PROMPT.md +70 -70
- package/src/check.ts +103 -103
- package/src/conda-environments.ts +5 -4
- package/src/config.ts +106 -80
- package/src/console/client.ts +132 -119
- package/src/contest.ts +96 -96
- package/src/display.ts +175 -175
- package/src/level-readout.ts +53 -53
- package/src/model.ts +126 -125
- package/src/readout.ts +63 -63
- package/src/request.ts +154 -154
- package/src/world.ts +592 -590
package/src/check.ts
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
export type CheckResult = '大成功' | '极难成功' | '困难成功' | '普通成功' | '普通失败' | '大失败';
|
|
2
|
-
|
|
3
|
-
/** 难度等级:1 = 困难、2 = 极难。省略表示基准难度。 */
|
|
4
|
-
export type Grade = 1 | 2;
|
|
5
|
-
|
|
6
|
-
/** 组合检定:and 要求每项都成功,or 任意一项成功即可。 */
|
|
7
|
-
export type Joint = 'and' | 'or';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 奖励/惩罚骰的自动判定阈值:|bonus| 达到它就不再调用判定模型,直接给出结论。
|
|
11
|
-
* agent 填的是合计值(情境修正 + 本项花掉的 banked 奖励骰),所以连压两颗 banked 骰
|
|
12
|
-
* 越过一次检定是可达的——这正是「用奖励骰跳过检定」的通路。
|
|
13
|
-
*/
|
|
14
|
-
export const AUTO_BONUS = 3;
|
|
15
|
-
|
|
16
|
-
export const GRADE_LABEL: Record<Grade, string> = { 1: '困难', 2: '极难' };
|
|
17
|
-
export const JOINT_LABEL: Record<Joint, string> = { and: '全部', or: '任一' };
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 难度是角色达成概率的百分数,直接取自标定后的水平估计。
|
|
21
|
-
* 结果钳在 1–99,可表达区间是 [1%, 99%];钳位不是可选项:允许 0 时掷出的 1 是大成功、
|
|
22
|
-
* 允许 100 时掷出的 100 是大失败,都会把实际成功概率从 0 或 1 挤开。
|
|
23
|
-
* 在 1–99 内且 grade 与 bonus 都是基准时,`classify` 的成功条件等价于 `roll <= difficulty`
|
|
24
|
-
* (1 <= 难度、100 > 难度恒成立),因此 P(成功) = 难度/100 精确成立,暴击不改变这个概率。
|
|
25
|
-
* 加上代偿之后这条不再成立:grade 把成功率压到约 难度/200(grade 1)或 难度/400(grade 2);
|
|
26
|
-
* bonus ≠ 0 时掷 1+|bonus| 颗取极值,成功率变成 1-(1-p)^n(优势)或 p^n(劣势)。
|
|
27
|
-
*/
|
|
28
|
-
export function difficultyFromLevel(level: number): number {
|
|
29
|
-
if (!Number.isFinite(level) || level < 0 || level > 100) {
|
|
30
|
-
throw new Error('模型返回的水平必须是 0–100 的有限数值');
|
|
31
|
-
}
|
|
32
|
-
return Math.max(1, Math.min(99, Math.round(level)));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 难度等级的覆盖表:阈值完全不动,只把落在某些区域里的档位改个标签。
|
|
37
|
-
* 大成功与大失败从不被覆盖——它们由骰值本身决定,不因为上游区域被占而改变。
|
|
38
|
-
* 于是 grade 1 下「困难成功」不再出现,grade 2 下「极难成功」也不再出现。
|
|
39
|
-
* 查表一律以基准档位为键,不做链式替换。
|
|
40
|
-
*/
|
|
41
|
-
const GRADE_OVERRIDES: Record<Grade, Partial<Record<CheckResult, CheckResult>>> = {
|
|
42
|
-
1: { 困难成功: '普通成功', 普通成功: '普通失败' },
|
|
43
|
-
2: { 极难成功: '普通成功', 困难成功: '普通失败', 普通成功: '普通失败' },
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export function classify(roll: number, difficulty: number, grade?: Grade): CheckResult {
|
|
47
|
-
if (!Number.isInteger(roll) || roll < 1 || roll > 100) throw new Error('骰值必须是 1–100 的整数');
|
|
48
|
-
if (roll === 1) return '大成功';
|
|
49
|
-
if (roll === 100) return '大失败';
|
|
50
|
-
const base: CheckResult = roll <= difficulty * 0.25 ? '极难成功'
|
|
51
|
-
: roll <= difficulty * 0.5 ? '困难成功'
|
|
52
|
-
: roll <= difficulty ? '普通成功' : '普通失败';
|
|
53
|
-
return grade === undefined ? base : GRADE_OVERRIDES[grade][base] ?? base;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface RollOutcome {
|
|
57
|
-
/** 1+|bonus| 颗,按掷出顺序。 */
|
|
58
|
-
dice: number[];
|
|
59
|
-
/** 用于判档的那一颗:优势取最低、劣势取最高。暴击只判这一颗。 */
|
|
60
|
-
chosen: number;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* 奖惩骰:掷 1+|bonus| 颗 d100,优势取最低、劣势取最高。
|
|
65
|
-
* bonus = 0 时只有一颗,与不带代偿的旧行为完全一致。
|
|
66
|
-
* 注意取极值会让暴击概率随骰数变化:优势下大失败要求每颗都是 100,劣势下大成功要求每颗都是 1。
|
|
67
|
-
*/
|
|
68
|
-
export function rollDice(roll: () => number, bonus: number): RollOutcome {
|
|
69
|
-
if (!Number.isInteger(bonus)) throw new Error('奖励/惩罚骰必须是整数');
|
|
70
|
-
const dice = Array.from({ length: 1 + Math.abs(bonus) }, () => roll());
|
|
71
|
-
const chosen = bonus > 0 ? Math.min(...dice) : bonus < 0 ? Math.max(...dice) : dice[0]!;
|
|
72
|
-
return { dice, chosen };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** 单项的自动判定:|bonus| 达到 AUTO_BONUS 就不调用判定模型。 */
|
|
76
|
-
export function autoVerdict(bonus: number): '成功' | '失败' | null {
|
|
77
|
-
if (bonus >= AUTO_BONUS) return '成功';
|
|
78
|
-
if (bonus <= -AUTO_BONUS) return '失败';
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** 组合检定的有效难度:and 取最小(每项都要过)、or 取最大(任意一项过即可)。 */
|
|
83
|
-
export function effectiveDifficulty(difficulties: number[], joint: Joint): number {
|
|
84
|
-
return joint === 'and' ? Math.min(...difficulties) : Math.max(...difficulties);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** 组合检定的有效奖惩:同样是 and 取最小、or 取最大,与难度规则同构。 */
|
|
88
|
-
export function effectiveBonus(bonuses: number[], joint: Joint): number {
|
|
89
|
-
return joint === 'and' ? Math.min(...bonuses) : Math.max(...bonuses);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** 建议文案跟显示标签走:grade 把区域改标成普通成功/普通失败之后,那一段就不再提示奖励。 */
|
|
93
|
-
export function adviceFor(result: CheckResult): string {
|
|
94
|
-
return result === '大成功' ? '给予额外奖励。'
|
|
95
|
-
: result === '大失败' ? '给予额外惩罚。'
|
|
96
|
-
: result === '极难成功' || result === '困难成功' ? '酌情(可选)给予小奖励。'
|
|
97
|
-
: '';
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** 档位高低:大成功 > 极难成功 > 困难成功 > 普通成功 > 普通失败 > 大失败。对抗检定按它比大小。 */
|
|
101
|
-
export const BAND_RANK: Record<CheckResult, number> = {
|
|
102
|
-
大成功: 6, 极难成功: 5, 困难成功: 4, 普通成功: 3, 普通失败: 2, 大失败: 1,
|
|
103
|
-
};
|
|
1
|
+
export type CheckResult = '大成功' | '极难成功' | '困难成功' | '普通成功' | '普通失败' | '大失败';
|
|
2
|
+
|
|
3
|
+
/** 难度等级:1 = 困难、2 = 极难。省略表示基准难度。 */
|
|
4
|
+
export type Grade = 1 | 2;
|
|
5
|
+
|
|
6
|
+
/** 组合检定:and 要求每项都成功,or 任意一项成功即可。 */
|
|
7
|
+
export type Joint = 'and' | 'or';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 奖励/惩罚骰的自动判定阈值:|bonus| 达到它就不再调用判定模型,直接给出结论。
|
|
11
|
+
* agent 填的是合计值(情境修正 + 本项花掉的 banked 奖励骰),所以连压两颗 banked 骰
|
|
12
|
+
* 越过一次检定是可达的——这正是「用奖励骰跳过检定」的通路。
|
|
13
|
+
*/
|
|
14
|
+
export const AUTO_BONUS = 3;
|
|
15
|
+
|
|
16
|
+
export const GRADE_LABEL: Record<Grade, string> = { 1: '困难', 2: '极难' };
|
|
17
|
+
export const JOINT_LABEL: Record<Joint, string> = { and: '全部', or: '任一' };
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 难度是角色达成概率的百分数,直接取自标定后的水平估计。
|
|
21
|
+
* 结果钳在 1–99,可表达区间是 [1%, 99%];钳位不是可选项:允许 0 时掷出的 1 是大成功、
|
|
22
|
+
* 允许 100 时掷出的 100 是大失败,都会把实际成功概率从 0 或 1 挤开。
|
|
23
|
+
* 在 1–99 内且 grade 与 bonus 都是基准时,`classify` 的成功条件等价于 `roll <= difficulty`
|
|
24
|
+
* (1 <= 难度、100 > 难度恒成立),因此 P(成功) = 难度/100 精确成立,暴击不改变这个概率。
|
|
25
|
+
* 加上代偿之后这条不再成立:grade 把成功率压到约 难度/200(grade 1)或 难度/400(grade 2);
|
|
26
|
+
* bonus ≠ 0 时掷 1+|bonus| 颗取极值,成功率变成 1-(1-p)^n(优势)或 p^n(劣势)。
|
|
27
|
+
*/
|
|
28
|
+
export function difficultyFromLevel(level: number): number {
|
|
29
|
+
if (!Number.isFinite(level) || level < 0 || level > 100) {
|
|
30
|
+
throw new Error('模型返回的水平必须是 0–100 的有限数值');
|
|
31
|
+
}
|
|
32
|
+
return Math.max(1, Math.min(99, Math.round(level)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 难度等级的覆盖表:阈值完全不动,只把落在某些区域里的档位改个标签。
|
|
37
|
+
* 大成功与大失败从不被覆盖——它们由骰值本身决定,不因为上游区域被占而改变。
|
|
38
|
+
* 于是 grade 1 下「困难成功」不再出现,grade 2 下「极难成功」也不再出现。
|
|
39
|
+
* 查表一律以基准档位为键,不做链式替换。
|
|
40
|
+
*/
|
|
41
|
+
const GRADE_OVERRIDES: Record<Grade, Partial<Record<CheckResult, CheckResult>>> = {
|
|
42
|
+
1: { 困难成功: '普通成功', 普通成功: '普通失败' },
|
|
43
|
+
2: { 极难成功: '普通成功', 困难成功: '普通失败', 普通成功: '普通失败' },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function classify(roll: number, difficulty: number, grade?: Grade): CheckResult {
|
|
47
|
+
if (!Number.isInteger(roll) || roll < 1 || roll > 100) throw new Error('骰值必须是 1–100 的整数');
|
|
48
|
+
if (roll === 1) return '大成功';
|
|
49
|
+
if (roll === 100) return '大失败';
|
|
50
|
+
const base: CheckResult = roll <= difficulty * 0.25 ? '极难成功'
|
|
51
|
+
: roll <= difficulty * 0.5 ? '困难成功'
|
|
52
|
+
: roll <= difficulty ? '普通成功' : '普通失败';
|
|
53
|
+
return grade === undefined ? base : GRADE_OVERRIDES[grade][base] ?? base;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface RollOutcome {
|
|
57
|
+
/** 1+|bonus| 颗,按掷出顺序。 */
|
|
58
|
+
dice: number[];
|
|
59
|
+
/** 用于判档的那一颗:优势取最低、劣势取最高。暴击只判这一颗。 */
|
|
60
|
+
chosen: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 奖惩骰:掷 1+|bonus| 颗 d100,优势取最低、劣势取最高。
|
|
65
|
+
* bonus = 0 时只有一颗,与不带代偿的旧行为完全一致。
|
|
66
|
+
* 注意取极值会让暴击概率随骰数变化:优势下大失败要求每颗都是 100,劣势下大成功要求每颗都是 1。
|
|
67
|
+
*/
|
|
68
|
+
export function rollDice(roll: () => number, bonus: number): RollOutcome {
|
|
69
|
+
if (!Number.isInteger(bonus)) throw new Error('奖励/惩罚骰必须是整数');
|
|
70
|
+
const dice = Array.from({ length: 1 + Math.abs(bonus) }, () => roll());
|
|
71
|
+
const chosen = bonus > 0 ? Math.min(...dice) : bonus < 0 ? Math.max(...dice) : dice[0]!;
|
|
72
|
+
return { dice, chosen };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 单项的自动判定:|bonus| 达到 AUTO_BONUS 就不调用判定模型。 */
|
|
76
|
+
export function autoVerdict(bonus: number): '成功' | '失败' | null {
|
|
77
|
+
if (bonus >= AUTO_BONUS) return '成功';
|
|
78
|
+
if (bonus <= -AUTO_BONUS) return '失败';
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 组合检定的有效难度:and 取最小(每项都要过)、or 取最大(任意一项过即可)。 */
|
|
83
|
+
export function effectiveDifficulty(difficulties: number[], joint: Joint): number {
|
|
84
|
+
return joint === 'and' ? Math.min(...difficulties) : Math.max(...difficulties);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 组合检定的有效奖惩:同样是 and 取最小、or 取最大,与难度规则同构。 */
|
|
88
|
+
export function effectiveBonus(bonuses: number[], joint: Joint): number {
|
|
89
|
+
return joint === 'and' ? Math.min(...bonuses) : Math.max(...bonuses);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 建议文案跟显示标签走:grade 把区域改标成普通成功/普通失败之后,那一段就不再提示奖励。 */
|
|
93
|
+
export function adviceFor(result: CheckResult): string {
|
|
94
|
+
return result === '大成功' ? '给予额外奖励。'
|
|
95
|
+
: result === '大失败' ? '给予额外惩罚。'
|
|
96
|
+
: result === '极难成功' || result === '困难成功' ? '酌情(可选)给予小奖励。'
|
|
97
|
+
: '';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** 档位高低:大成功 > 极难成功 > 困难成功 > 普通成功 > 普通失败 > 大失败。对抗检定按它比大小。 */
|
|
101
|
+
export const BAND_RANK: Record<CheckResult, number> = {
|
|
102
|
+
大成功: 6, 极难成功: 5, 困难成功: 4, 普通成功: 3, 普通失败: 2, 大失败: 1,
|
|
103
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { homedir, platform } from 'node:os';
|
|
3
|
-
import {
|
|
3
|
+
import { join, posix, win32 } from 'node:path';
|
|
4
4
|
|
|
5
5
|
export interface CondaPythonOption {
|
|
6
6
|
value: string;
|
|
@@ -12,16 +12,17 @@ export function listCondaPythonOptions(
|
|
|
12
12
|
exists: (path: string) => boolean = existsSync,
|
|
13
13
|
targetPlatform: NodeJS.Platform = platform(),
|
|
14
14
|
): CondaPythonOption[] {
|
|
15
|
-
const
|
|
15
|
+
const paths = targetPlatform === 'win32' ? win32 : posix;
|
|
16
|
+
const executable = targetPlatform === 'win32' ? 'python.exe' : paths.join('bin', 'python');
|
|
16
17
|
const seen = new Set<string>();
|
|
17
18
|
const options: CondaPythonOption[] = [];
|
|
18
19
|
for (const line of manifest.split(/\r?\n/u)) {
|
|
19
20
|
const prefix = line.trim();
|
|
20
21
|
if (!prefix || seen.has(prefix)) continue;
|
|
21
22
|
seen.add(prefix);
|
|
22
|
-
const pythonExecutable = join(prefix, executable);
|
|
23
|
+
const pythonExecutable = paths.join(prefix, executable);
|
|
23
24
|
if (!exists(pythonExecutable)) continue;
|
|
24
|
-
options.push({ value: pythonExecutable, label: `${basename(prefix)} · ${prefix}` });
|
|
25
|
+
options.push({ value: pythonExecutable, label: `${paths.basename(prefix)} · ${prefix}` });
|
|
25
26
|
}
|
|
26
27
|
return options;
|
|
27
28
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,80 +1,106 @@
|
|
|
1
|
-
import type { ConfigGroup } from 'cortico/core/types.ts';
|
|
2
|
-
|
|
3
|
-
export type CheckBackend = 'laya' | 'laya-multilingual' | 'jev';
|
|
4
|
-
export type JevSource = 'typesafe' | 'openrouter' | 'custom';
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
jevSource
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
'
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'x-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
1
|
+
import type { ConfigGroup } from 'cortico/core/types.ts';
|
|
2
|
+
|
|
3
|
+
export type CheckBackend = 'laya' | 'laya-multilingual' | 'jev';
|
|
4
|
+
export type JevSource = 'typesafe' | 'openrouter' | 'custom';
|
|
5
|
+
export const DEFAULT_DECISION_MODELS = { typesafe: 'jev-latest', openrouter: '~typesafe/jev-latest', custom: 'jev-latest' };
|
|
6
|
+
export const OPENROUTER_LUNA_MODEL = 'openai/gpt-6-luna-decisions';
|
|
7
|
+
|
|
8
|
+
export function decisionModel(config: Pick<SimpleTrpgCheckConfigSection, 'jevSource' | 'jevModel'>): string {
|
|
9
|
+
return config.jevModel?.trim() || DEFAULT_DECISION_MODELS[config.jevSource];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function decisionModelPresets(source: JevSource): Array<{ value: string; label: string }> {
|
|
13
|
+
return [
|
|
14
|
+
{ value: DEFAULT_DECISION_MODELS[source], label: 'JEV' },
|
|
15
|
+
...(source === 'openrouter' ? [{ value: OPENROUTER_LUNA_MODEL, label: 'Luna Decisions' }] : []),
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function hasCalibratedReadout(model: string): boolean {
|
|
20
|
+
return Object.values(DEFAULT_DECISION_MODELS).includes(model);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SimpleTrpgCheckConfigSection {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
backend: CheckBackend;
|
|
26
|
+
jevSource: JevSource;
|
|
27
|
+
jevEndpoint: string;
|
|
28
|
+
jevModel?: string;
|
|
29
|
+
forceMultilingual: boolean;
|
|
30
|
+
pythonExecutable: string;
|
|
31
|
+
layaIdleTtlMinutes: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const SIMPLE_TRPG_CHECK_DEFAULTS: SimpleTrpgCheckConfigSection = {
|
|
35
|
+
enabled: false,
|
|
36
|
+
backend: 'laya-multilingual',
|
|
37
|
+
jevSource: 'typesafe',
|
|
38
|
+
jevEndpoint: '',
|
|
39
|
+
jevModel: '',
|
|
40
|
+
forceMultilingual: false,
|
|
41
|
+
pythonExecutable: 'python',
|
|
42
|
+
layaIdleTtlMinutes: 10,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const SIMPLE_TRPG_CHECK_TYPESAFE_SECRET = 'CORTICO_JEV_TYPESAFE_API_KEY';
|
|
46
|
+
export const SIMPLE_TRPG_CHECK_OPENROUTER_SECRET = 'CORTICO_JEV_OPENROUTER_API_KEY';
|
|
47
|
+
export const SIMPLE_TRPG_CHECK_CUSTOM_SECRET = 'CORTICO_JEV_API_KEY';
|
|
48
|
+
export const SIMPLE_TRPG_CHECK_LEGACY_TYPESAFE_SECRET = 'CORTICO_SIMPLE_TRPG_CHECK_TYPESAFE_API_KEY';
|
|
49
|
+
export const SIMPLE_TRPG_CHECK_LEGACY_OPENROUTER_SECRET = 'CORTICO_SIMPLE_TRPG_CHECK_OPENROUTER_API_KEY';
|
|
50
|
+
|
|
51
|
+
export const SIMPLE_TRPG_CHECK_CONFIG_GROUP: ConfigGroup = {
|
|
52
|
+
id: 'world:simple-trpg-check',
|
|
53
|
+
owner: 'world:simple-trpg-check',
|
|
54
|
+
schema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
title: 'Simple TRPG Check',
|
|
57
|
+
properties: {
|
|
58
|
+
'worlds.simple-trpg-check.backend': {
|
|
59
|
+
type: 'string',
|
|
60
|
+
title: '判定方式',
|
|
61
|
+
enum: ['laya-multilingual', 'laya', 'jev'],
|
|
62
|
+
'x-hot': false,
|
|
63
|
+
},
|
|
64
|
+
'worlds.simple-trpg-check.jevSource': {
|
|
65
|
+
type: 'string',
|
|
66
|
+
title: '决策服务',
|
|
67
|
+
enum: ['typesafe', 'openrouter', 'custom'],
|
|
68
|
+
'x-hot': true,
|
|
69
|
+
},
|
|
70
|
+
'worlds.simple-trpg-check.jevModel': {
|
|
71
|
+
type: 'string',
|
|
72
|
+
title: '决策模型',
|
|
73
|
+
description: '留空使用该服务的默认 JEV 模型。',
|
|
74
|
+
'x-hot': true,
|
|
75
|
+
},
|
|
76
|
+
'worlds.simple-trpg-check.jevEndpoint': {
|
|
77
|
+
type: 'string',
|
|
78
|
+
title: '自定义决策服务地址',
|
|
79
|
+
description: '服务须接受 SystemOne 的 state、questions 并返回按问题名索引的 answers。',
|
|
80
|
+
'x-hot': true,
|
|
81
|
+
},
|
|
82
|
+
'worlds.simple-trpg-check.forceMultilingual': {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
title: '强制适配多语言',
|
|
85
|
+
description: '要求 agent 用英文填写 scenario 与 skill_lists;不翻译输入,也不切换模型。',
|
|
86
|
+
'x-hot': true,
|
|
87
|
+
},
|
|
88
|
+
'worlds.simple-trpg-check.pythonExecutable': {
|
|
89
|
+
type: 'string',
|
|
90
|
+
title: '多语言 Laya Python 环境',
|
|
91
|
+
'x-options': 'simple-trpg-check-conda-python',
|
|
92
|
+
description: '从本机 Conda 环境中选择;该环境需安装 laya。',
|
|
93
|
+
'x-hot': false,
|
|
94
|
+
},
|
|
95
|
+
'worlds.simple-trpg-check.layaIdleTtlMinutes': {
|
|
96
|
+
type: 'integer',
|
|
97
|
+
title: 'Laya 空闲释放时间',
|
|
98
|
+
minimum: 0,
|
|
99
|
+
multipleOf: 1,
|
|
100
|
+
'x-suffix': 'min',
|
|
101
|
+
'x-hot': true,
|
|
102
|
+
description: '最后一次本地评分后保留模型的分钟数;与其他使用者共用时取最长 TTL。',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
} as ConfigGroup;
|