cortico 0.1.0 → 0.1.1
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 +4 -11
- package/package.json +1 -3
- package/src/bot.ts +1 -1
- package/src/core/README.md +0 -4
- package/src/core/types.ts +1 -1
- package/src/core/util.ts +2 -1
- package/src/deploy.ts +8 -6
- package/src/providers/README.md +4 -5
- package/src/providers/base.ts +1 -1
- package/src/providers/configuration.ts +2 -1
- package/src/providers/console/settings.ts +8 -10
- package/src/providers/llamacpp/console/runtime-panel.ts +73 -32
- package/src/providers/llamacpp/console/server.ts +1 -20
- package/src/providers/llamacpp/index.ts +0 -2
- package/src/providers/llamacpp/options.ts +0 -2
- package/src/providers/llamacpp/strings.ts +6 -6
- package/src/providers/openai-responses-compat/index.ts +0 -3
- package/src/web/README.md +1 -5
- package/src/web/client/console-pages/builtins/llm-settings/panel.ts +35 -41
- package/src/web/client/console-pages/builtins/llm-settings/strings.ts +4 -4
- package/src/web/client/console-pages/host.ts +2 -2
- package/src/web/client/features/core/strings.ts +2 -2
- package/src/web/client/features/feature.ts +1 -1
- package/src/web/client/features/live/index.ts +8 -1
- package/src/web/client/features/live/onboarding.ts +4 -1
- package/src/web/client/features/live/strings.ts +11 -11
- package/src/web/client/features/live/timeline.ts +2 -1
- package/src/web/client/features/settings/general.ts +1 -0
- package/src/web/client/features/settings/index.ts +1 -0
- package/src/web/client/features/worlds/index.ts +1 -0
- package/src/web/client/shell/index.ts +27 -47
- package/src/web/client/shell/strings.ts +0 -22
- package/src/web/client/ui/icons.ts +5 -0
- package/src/web/console-pages.ts +1 -1
- package/src/web/public/styles.css +21 -8
- package/src/web/server.ts +11 -3
- package/src/web/shared/client-panel.ts +1 -1
- package/src/web/shared/console-protocol.ts +2 -1
- package/src/worlds/minecraft/README.md +61 -11
- package/src/worlds/minecraft/cell-facts.ts +226 -0
- package/src/worlds/minecraft/chests.ts +5 -0
- package/src/worlds/minecraft/containers.ts +325 -0
- package/src/worlds/minecraft/entity-facts.ts +31 -5
- package/src/worlds/minecraft/executor.ts +283 -10348
- package/src/worlds/minecraft/inventory.ts +268 -0
- package/src/worlds/minecraft/melee.ts +419 -0
- package/src/worlds/minecraft/placed-ledger.ts +152 -0
- package/src/worlds/minecraft/placement.ts +1038 -0
- package/src/worlds/minecraft/receipt.ts +340 -0
- package/src/worlds/minecraft/skill-context.ts +358 -0
- package/src/worlds/minecraft/skills-build.ts +1169 -0
- package/src/worlds/minecraft/skills-container.ts +1343 -0
- package/src/worlds/minecraft/skills-craft.ts +333 -0
- package/src/worlds/minecraft/skills-dig.ts +624 -0
- package/src/worlds/minecraft/skills-gather.ts +1230 -0
- package/src/worlds/minecraft/skills-interact.ts +1559 -0
- package/src/worlds/minecraft/tools.ts +331 -0
- package/src/worlds/minecraft/travel.ts +763 -0
- package/src/worlds/minecraft/until.ts +75 -0
- package/src/worlds/terminal/world.ts +8 -5
- package/src/providers/console/config.ts +0 -21
- package/src/providers/llamacpp/config.ts +0 -36
- package/src/providers/openai-responses-compat/config.ts +0 -20
package/README.md
CHANGED
|
@@ -15,8 +15,7 @@ import type { EventEnvelope, WorldHost } from 'cortico/core/types.ts';
|
|
|
15
15
|
|
|
16
16
|
Inside a running instance those specifiers are resolved by the host's module hook to the
|
|
17
17
|
framework that instance runs. This package supplies the same files to the editor, the type
|
|
18
|
-
checker and the test runner
|
|
19
|
-
what it does: it has no entry point and starts nothing.
|
|
18
|
+
checker and the test runner. It has no entry point and starts nothing.
|
|
20
19
|
|
|
21
20
|
## Install
|
|
22
21
|
|
|
@@ -34,14 +33,8 @@ the packaging rules.
|
|
|
34
33
|
|
|
35
34
|
## Running a bot
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
git clone https://github.com/Pal-AI-Lab/Cortico.git
|
|
42
|
-
cd Cortico
|
|
43
|
-
corepack pnpm install
|
|
44
|
-
pnpm start
|
|
45
|
-
```
|
|
36
|
+
Deployments, bot packages and the web console live in a checkout of the repository, which is
|
|
37
|
+
where a bot runs from:
|
|
38
|
+
[Quick Start](https://github.com/Pal-AI-Lab/Cortico#quick-start).
|
|
46
39
|
|
|
47
40
|
MIT licensed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A TypeScript framework for event-driven, long-running persona bots",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
"@types/express": "^4.17.21",
|
|
37
37
|
"@types/ws": "^8.5.13",
|
|
38
38
|
"express": "^4.21.2",
|
|
39
|
-
"fflate": "0.8.3",
|
|
40
|
-
"tar-stream": "3.2.1",
|
|
41
39
|
"ws": "^8.18.0"
|
|
42
40
|
}
|
|
43
41
|
}
|
package/src/bot.ts
CHANGED
|
@@ -867,7 +867,7 @@ export function deriveConsolePageSources(
|
|
|
867
867
|
parts: { assembly: WorldAssembly; persona?: Persona },
|
|
868
868
|
/**
|
|
869
869
|
* bot 标识、展示名、Memory 名与配置组;配置组归入 Persona 页面。
|
|
870
|
-
*
|
|
870
|
+
* 展示名是这一台 bot 的名字(底栏头像旁边那个),Persona 页的标题另取 Persona 的类名。
|
|
871
871
|
*/
|
|
872
872
|
bot?: { id: string; label: string; memoryName?: string; configGroups?: readonly ConfigGroup[] },
|
|
873
873
|
|
package/src/core/README.md
CHANGED
|
@@ -77,12 +77,8 @@ handler 异常转为失败回执。流式生成时 `EagerDispatch` 可提前执
|
|
|
77
77
|
`Persona.onHandoff(snapshot, { hardTokens })` 返回 `{ tail, trim? }`。Core 重建 system 前缀,
|
|
78
78
|
按 `hardTokens − estimate(prefix)` 限制保留上下文,其中 prefix 包括 system 前缀和 Persona 的合成
|
|
79
79
|
开头(`sessionHead()`)。Core 校验工具调用配对,并重置 session。
|
|
80
|
-
合成开头每次请求现取,不写入持久 session;Core 不据此判定远端缓存是否命中。
|
|
81
80
|
阶段预算与保留比例由 Persona 决定,模型配置来自当前 provider。
|
|
82
81
|
|
|
83
|
-
存储清单使用 `StorageOwner` 标识 Core、Persona、Memory 与 World 的归属。
|
|
84
|
-
页面组织和 Memory 展示名称由装配层处理。
|
|
85
|
-
|
|
86
82
|
## 错误隔离
|
|
87
83
|
|
|
88
84
|
装配层将 World 构造失败限制在该 World。`start()` 抛错时不挂载;`stop()` 失败或超时会记录
|
package/src/core/types.ts
CHANGED
|
@@ -930,7 +930,7 @@ export interface Persona {
|
|
|
930
930
|
/**
|
|
931
931
|
* session 的合成开头:每次请求置于 system 前缀之后、持久历史之前,不写入 session,交接时
|
|
932
932
|
* 不进保留内容。Core 每次出请求前调用一次,丢弃 system 与 developer 项,补齐工具配对,
|
|
933
|
-
* 计入 hardTokens
|
|
933
|
+
* 计入 hardTokens。内容只应随 Persona 自己的输入变化;每次不同就每次打穿前缀缓存。
|
|
934
934
|
*/
|
|
935
935
|
sessionHead?(): Item[];
|
|
936
936
|
/** Memory 实例。Core 不读它的内容;bot 没给 memoryName 时控制台以它的类名作 Memory 页标题。 */
|
package/src/core/util.ts
CHANGED
|
@@ -372,7 +372,8 @@ export function shortId(prefix = ''): string {
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
/**
|
|
375
|
-
*
|
|
375
|
+
* 读一个手写的文本文件:按 BOM 选编码,UTF-8 与 UTF-16 LE 都读得出来,BOM 不进返回值。
|
|
376
|
+
* 没有 BOM 就是 UTF-8。Windows 的 shell 重定向写出的是带 BOM 的 UTF-8 或 UTF-16 LE。
|
|
376
377
|
*/
|
|
377
378
|
export function readTextFile(file: string): string {
|
|
378
379
|
const bytes = readFileSync(file);
|
package/src/deploy.ts
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* 部署文件中的 providers 字段不参与合并。Core、Persona 与 World 的默认值由各自所有者提供。
|
|
4
4
|
* 运行时共享合并后的配置对象,控制台和调参工具原位更新。
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { isAbsolute, resolve } from 'node:path';
|
|
8
8
|
import type { CoreConfig, LLMProviderEntry } from './core/types.ts';
|
|
9
9
|
import { deepMerge, type LoadedConfig } from './core/config.ts';
|
|
10
10
|
import { secretReader } from './core/secrets.ts';
|
|
11
|
-
import { readTextFile } from './core/util.ts';
|
|
12
11
|
import { deploymentRoot, repoRoot as codeRepoRoot } from './paths.ts';
|
|
13
12
|
|
|
14
13
|
export type { LoadedConfig } from './core/config.ts';
|
|
@@ -27,7 +26,10 @@ export function listBots(root = deploymentRoot()): string[] {
|
|
|
27
26
|
/** 部署根下一份部署都没有时自建的那份,以及它引用的 bot 代码包。 */
|
|
28
27
|
export const DEFAULT_DEPLOYMENT = { name: 'mybot', bot: 'cormini' } as const;
|
|
29
28
|
|
|
30
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* 开场引导的一次性标记,放在部署目录下。自建部署时写入,控制台见到它才给引导,操作员
|
|
31
|
+
* 开口或按下那颗按钮后删除。手动建的部署没有这个文件,也就不会看到引导。
|
|
32
|
+
*/
|
|
31
33
|
export const ONBOARDING_FLAG_FILE = '.onboarding';
|
|
32
34
|
|
|
33
35
|
/** 部署根下另有用途的目录名,不能拿来当部署名。 */
|
|
@@ -88,7 +90,7 @@ function globalProviders(providersDir: string): Record<string, LLMProviderEntry>
|
|
|
88
90
|
const file = resolve(providersDir, name, 'config.json');
|
|
89
91
|
if (!existsSync(file)) continue;
|
|
90
92
|
try {
|
|
91
|
-
table[name] = JSON.parse(
|
|
93
|
+
table[name] = JSON.parse(readFileSync(file, 'utf8')) as LLMProviderEntry;
|
|
92
94
|
} catch (err) {
|
|
93
95
|
throw new Error(`${file} 解析失败:${err instanceof Error ? err.message : String(err)}`);
|
|
94
96
|
}
|
|
@@ -108,7 +110,7 @@ function packageWorldOverrides(pkgDir: string): Record<string, unknown> {
|
|
|
108
110
|
const file = resolve(ioDir, id, 'config.json');
|
|
109
111
|
if (!existsSync(file)) continue;
|
|
110
112
|
try {
|
|
111
|
-
worlds[id] = JSON.parse(
|
|
113
|
+
worlds[id] = JSON.parse(readFileSync(file, 'utf8'));
|
|
112
114
|
} catch (err) {
|
|
113
115
|
throw new Error(`${file} 解析失败:${err instanceof Error ? err.message : String(err)}`);
|
|
114
116
|
}
|
|
@@ -135,7 +137,7 @@ export function loadDeployment<C extends CoreConfig>(
|
|
|
135
137
|
const providers = resolve(providersDir);
|
|
136
138
|
const cfgPath = resolve(dir, 'config.json');
|
|
137
139
|
const raw: Partial<C> & Record<string, unknown> = existsSync(cfgPath)
|
|
138
|
-
? (JSON.parse(
|
|
140
|
+
? (JSON.parse(readFileSync(cfgPath, 'utf8')) as Partial<C> & Record<string, unknown>)
|
|
139
141
|
: {};
|
|
140
142
|
// 部署文件不能覆盖共享端点表。
|
|
141
143
|
delete raw.providers;
|
package/src/providers/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Owner: src/providers/base.ts, src/providers/registry.ts
|
|
1
|
+
<!-- Owner: src/providers/base.ts, src/providers/registry.ts -->
|
|
2
2
|
|
|
3
3
|
# src/providers
|
|
4
4
|
|
|
@@ -27,10 +27,9 @@ effort 收任意非空串)、`serviceTiers`、`create(name, entry, host)`。可
|
|
|
27
27
|
`validateEntry()`、`validateModel()`、`accepts()`(多模态判定)、`config()` 与 `console()`
|
|
28
28
|
(附加配置组与面板)、`prices()`、`estimateTokens()`、`contextOverflow()`。
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
配置组仍参与服务端校验。运行时启停和模型操作由面板 invoke 提供。
|
|
30
|
+
地址、密钥变量名与图像开关由控制台的端点面板编辑,模块不为它们声明配置组。模块自己的
|
|
31
|
+
`options.*` 要么走 `config()` 的配置组,要么由 `console()` 声明一块挂进 `instance` 插槽的面板——
|
|
32
|
+
内建 llamacpp 的运行时与模型两段走的是后者。
|
|
34
33
|
|
|
35
34
|
`create()` 返回 `ProviderInstance`:`client`(实现 `respond`)、`listModels?`、`control?`、
|
|
36
35
|
`compatibilityKey?`、`start?` / `stop?`、`contextWindow?(model)`。
|
package/src/providers/base.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface ProviderInstance {
|
|
|
60
60
|
contextWindow?(model: string): number | undefined;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/**
|
|
63
|
+
/** 一个端点此刻能不能发起生成。判断只看本地状态,不连上游。 */
|
|
64
64
|
export interface ProviderAvailability {
|
|
65
65
|
ready: boolean;
|
|
66
66
|
/** 不可用的原因,控制台语言;可用时不带。 */
|
|
@@ -27,14 +27,13 @@ import { responseRequest } from '../../protocol/open-responses/context-helpers.t
|
|
|
27
27
|
import { record } from '../../protocol/open-responses/context.ts';
|
|
28
28
|
import { text } from './strings.ts';
|
|
29
29
|
import type { ProviderConsoleHost } from './types.ts';
|
|
30
|
-
import { connectionGroup } from './config.ts';
|
|
31
30
|
|
|
32
31
|
export type SecretStatus = 'env' | 'file' | 'none';
|
|
33
32
|
|
|
34
33
|
/** Output token limit used by the connectivity probe. */
|
|
35
34
|
const PROBE_MAX_OUTPUT_TOKENS = 256;
|
|
36
35
|
|
|
37
|
-
/**
|
|
36
|
+
/** 密钥变量名由操作员自由填写,拼进正则前按字面转义。 */
|
|
38
37
|
function escapeRegExp(s: string): string {
|
|
39
38
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
40
39
|
}
|
|
@@ -71,8 +70,7 @@ export class ProviderSettings {
|
|
|
71
70
|
private declaredGroups(language: Language) {
|
|
72
71
|
return this.modules.flatMap((module) =>
|
|
73
72
|
this.entries(module).flatMap(({ name, entry }) =>
|
|
74
|
-
|
|
75
|
-
.map((group) => ({ name, group })),
|
|
73
|
+
(module.config?.(name, entry, language) ?? []).map((group) => ({ name, group })),
|
|
76
74
|
),
|
|
77
75
|
);
|
|
78
76
|
}
|
|
@@ -250,6 +248,7 @@ export class ProviderSettings {
|
|
|
250
248
|
})) satisfies ConsolePageSource[];
|
|
251
249
|
}
|
|
252
250
|
|
|
251
|
+
/** 端点能不能用:通用条件由框架查,模块自己的条件由模块答。 */
|
|
253
252
|
availability(name: string, language: Language = 'zh'): ProviderAvailability {
|
|
254
253
|
const entry = this.config.providers[name];
|
|
255
254
|
if (!entry) return { ready: false, reason: text(language).unknownInstance };
|
|
@@ -263,7 +262,8 @@ export class ProviderSettings {
|
|
|
263
262
|
}
|
|
264
263
|
|
|
265
264
|
/**
|
|
266
|
-
*
|
|
265
|
+
* 这批端点里有没有一个能用。灯亮=有;悬停说明给出第一个可用的端点名,
|
|
266
|
+
* 或者最后一个端点的不可用原因。
|
|
267
267
|
*/
|
|
268
268
|
private availableLamp(
|
|
269
269
|
entries: ReadonlyArray<{ name: string }>,
|
|
@@ -281,7 +281,7 @@ export class ProviderSettings {
|
|
|
281
281
|
return { label: S.availableLamp, state: 'offline', hint: reason };
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
/**
|
|
284
|
+
/** 所有模块合起来有没有一个可用端点。控制台左栏「语言模型」那一行点的就是它。 */
|
|
285
285
|
providersLamp(language: Language = 'zh'): ConsoleLamp {
|
|
286
286
|
return this.availableLamp(
|
|
287
287
|
this.modules.flatMap((module) => this.entries(module)),
|
|
@@ -334,7 +334,7 @@ export class ProviderSettings {
|
|
|
334
334
|
},
|
|
335
335
|
...(extra.panels ?? []),
|
|
336
336
|
],
|
|
337
|
-
config:
|
|
337
|
+
config: entries.flatMap(
|
|
338
338
|
({ name, entry }) => module.config?.(name, entry, language) ?? [],
|
|
339
339
|
),
|
|
340
340
|
invoke: async (panel, method, args) => {
|
|
@@ -358,8 +358,6 @@ export class ProviderSettings {
|
|
|
358
358
|
instances: this.entries(module).map(({ name, entry }) => ({
|
|
359
359
|
name,
|
|
360
360
|
entry,
|
|
361
|
-
config: this.declaredGroups(language).filter((item) => item.name === name)
|
|
362
|
-
.map(({ group }) => ({ group, values: this.values(group.id, language) })),
|
|
363
361
|
secretConfigured: this.secretStatus(name, entry),
|
|
364
362
|
quotes: (entry.spec ? [entry.spec] : []).map((spec) => ({
|
|
365
363
|
model: spec.model,
|
|
@@ -385,7 +383,7 @@ export class ProviderSettings {
|
|
|
385
383
|
const name = body.name;
|
|
386
384
|
if (method === 'create') {
|
|
387
385
|
this.assertNewName(name, language);
|
|
388
|
-
//
|
|
386
|
+
// 报价留空:用量页把这条端点的调用记成未计价,而不是零元。
|
|
389
387
|
this.save(name, {
|
|
390
388
|
kind: module.id,
|
|
391
389
|
baseUrl: String(body.baseUrl || module.defaultBaseUrl || ''),
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* The endpoint comes from `ctx.scope.instance`; edits are written on change.
|
|
4
4
|
*/
|
|
5
5
|
import type { ConsolePanel, ConsolePanelContext } from '../../../web/shared/client-panel.ts';
|
|
6
|
-
import type {
|
|
7
|
-
import {
|
|
8
|
-
import type { RuntimePanelState } from './server.ts';
|
|
6
|
+
import type { LaunchOptions } from '../options.ts';
|
|
7
|
+
import type { RuntimeState } from '../runtime.ts';
|
|
9
8
|
import { panel } from '../strings.ts';
|
|
10
9
|
|
|
11
|
-
type Row =
|
|
10
|
+
type Row = RuntimeState & { name: string };
|
|
12
11
|
|
|
13
12
|
const POLL_MS = 2_000;
|
|
14
13
|
|
|
@@ -43,31 +42,18 @@ export const runtimePanel: ConsolePanel = {
|
|
|
43
42
|
await load(true);
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function renderConfig(row: Row, index: number, body: HTMLElement): void {
|
|
61
|
-
const { group, values } = row.config[index];
|
|
62
|
-
body.append(ui.section(group.schema.title));
|
|
63
|
-
for (const [path, property] of Object.entries(group.schema.properties)) {
|
|
64
|
-
const field = configField(ui, property, values[path], () => {
|
|
65
|
-
if (field.read) void saveConfig(group.id, { [path]: field.read() });
|
|
66
|
-
}, ctx.signal);
|
|
67
|
-
field.node.setAttribute('aria-label', property.title);
|
|
68
|
-
body.append(ui.field(property.title, field.node));
|
|
69
|
-
if (property.description) body.append(ui.msgline(property.description));
|
|
70
|
-
}
|
|
45
|
+
/** One launch field; empty input sends NaN so the endpoint reports the same error as a bad value. */
|
|
46
|
+
function launchNumber(row: Row, key: keyof LaunchOptions, label: string, min: number): HTMLElement {
|
|
47
|
+
const current = row.launch?.[key];
|
|
48
|
+
const input = ui.input({
|
|
49
|
+
type: 'number',
|
|
50
|
+
value: current === undefined ? '' : String(current),
|
|
51
|
+
onChange: (value) => void act('configure', { launch: { [key]: Number(value) } }),
|
|
52
|
+
});
|
|
53
|
+
input.min = String(min);
|
|
54
|
+
input.step = '1';
|
|
55
|
+
input.setAttribute('aria-label', label);
|
|
56
|
+
return ui.field(label, input);
|
|
71
57
|
}
|
|
72
58
|
|
|
73
59
|
function renderUnmanaged(row: Row, body: HTMLElement): void {
|
|
@@ -88,8 +74,42 @@ export const runtimePanel: ConsolePanel = {
|
|
|
88
74
|
: install.phase === 'extracting' ? ui.pill(S.extracting, 'plain')
|
|
89
75
|
: ui.pill(S.absent, 'off');
|
|
90
76
|
|
|
91
|
-
|
|
92
|
-
|
|
77
|
+
body.append(ui.section(S.runtimeSection, S.runtimeSectionDesc));
|
|
78
|
+
const release = ui.input({
|
|
79
|
+
value: row.release ?? '',
|
|
80
|
+
cls: 'mono',
|
|
81
|
+
onChange: (value) => void act('configure', { release: value }),
|
|
82
|
+
});
|
|
83
|
+
release.setAttribute('aria-label', S.release);
|
|
84
|
+
const backend = ui.select({
|
|
85
|
+
value: row.backend ?? '',
|
|
86
|
+
options: row.backendChoices,
|
|
87
|
+
onChange: (value) => void act('configure', { backend: value }),
|
|
88
|
+
});
|
|
89
|
+
backend.setAttribute('aria-label', S.backend);
|
|
90
|
+
backend.disabled = row.own;
|
|
91
|
+
const dir = ui.input({
|
|
92
|
+
value: row.own ? row.runtimeDir ?? '' : '',
|
|
93
|
+
cls: 'mono',
|
|
94
|
+
onChange: (value) => void act('configure', { runtimeDir: value }),
|
|
95
|
+
});
|
|
96
|
+
dir.setAttribute('aria-label', S.runtimeDir);
|
|
97
|
+
const dirRow = ui.rowbar();
|
|
98
|
+
dirRow.append(dir, ui.button(S.browse, {
|
|
99
|
+
size: 'sm',
|
|
100
|
+
onClick: async () => {
|
|
101
|
+
const picked = await ctx.pickPath({ kind: 'directory', title: S.runtimeDir, currentPath: dir.value });
|
|
102
|
+
if (picked) await act('configure', { runtimeDir: picked });
|
|
103
|
+
},
|
|
104
|
+
}));
|
|
105
|
+
body.append(
|
|
106
|
+
ui.field(S.release, release),
|
|
107
|
+
ui.msgline(S.releaseHint),
|
|
108
|
+
ui.field(S.backend, backend),
|
|
109
|
+
ui.field(S.runtimeDir, dirRow),
|
|
110
|
+
ui.msgline(S.runtimeDirHint),
|
|
111
|
+
ui.kv([{ k: S.installStatus, v: installPill }]),
|
|
112
|
+
);
|
|
93
113
|
if (row.smartAppControl === 1) body.append(ui.msgline(S.sacWarning, true));
|
|
94
114
|
if (install.phase === 'downloading' || install.phase === 'extracting') {
|
|
95
115
|
const progress = ui.progress({
|
|
@@ -113,7 +133,28 @@ export const runtimePanel: ConsolePanel = {
|
|
|
113
133
|
installBar.append(ui.h('span', 'grow'), ui.button(S.disable, { onClick: () => void act('disable') }));
|
|
114
134
|
body.append(installBar);
|
|
115
135
|
|
|
116
|
-
|
|
136
|
+
body.append(ui.section(S.launchSection, S.launchSectionDesc));
|
|
137
|
+
const launchRow = ui.rowbar();
|
|
138
|
+
launchRow.append(
|
|
139
|
+
launchNumber(row, 'contextSize', S.contextSize, 1),
|
|
140
|
+
launchNumber(row, 'nGpuLayers', S.nGpuLayers, 0),
|
|
141
|
+
launchNumber(row, 'parallel', S.parallel, 1),
|
|
142
|
+
);
|
|
143
|
+
const extraArgs = ui.input({
|
|
144
|
+
value: row.launch?.extraArgs ?? '',
|
|
145
|
+
cls: 'mono',
|
|
146
|
+
onChange: (value) => void act('configure', { launch: { extraArgs: value } }),
|
|
147
|
+
});
|
|
148
|
+
extraArgs.setAttribute('aria-label', S.extraArgs);
|
|
149
|
+
body.append(
|
|
150
|
+
launchRow,
|
|
151
|
+
ui.field(S.extraArgs, extraArgs),
|
|
152
|
+
ui.msgline(S.extraArgsHint),
|
|
153
|
+
ui.checkbox(S.autoStart, {
|
|
154
|
+
checked: row.autoStart,
|
|
155
|
+
onChange: (checked) => void act('configure', { autoStart: checked }),
|
|
156
|
+
}).el,
|
|
157
|
+
);
|
|
117
158
|
|
|
118
159
|
body.append(ui.section(S.serverSection, S.serverSectionDesc));
|
|
119
160
|
const server = row.server;
|
|
@@ -8,9 +8,6 @@ import type { ProviderConsoleHost } from '../../console/types.ts';
|
|
|
8
8
|
import type { RouterCatalog, RouterModel } from '../catalog.ts';
|
|
9
9
|
import { LAUNCH_DEFAULTS, PINNED_RELEASE, backendChoices, defaultBackend, llamacppOptions, type LaunchOptions } from '../options.ts';
|
|
10
10
|
import type { LlamaRuntime } from '../runtime.ts';
|
|
11
|
-
import type { RuntimeState } from '../runtime.ts';
|
|
12
|
-
import { runtimeConfig } from '../config.ts';
|
|
13
|
-
import { getByPath, type ConfigGroup, type ConfigValues } from '../../../core/config-schema.ts';
|
|
14
11
|
import { text } from '../strings.ts';
|
|
15
12
|
|
|
16
13
|
interface Control {
|
|
@@ -26,11 +23,6 @@ export interface ModelsState {
|
|
|
26
23
|
models: RouterModel[];
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
export type RuntimePanelState = RuntimeState & {
|
|
30
|
-
name: string;
|
|
31
|
-
config: Array<{ group: ConfigGroup; values: ConfigValues }>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
26
|
/** The launch fields the panel edits, each optional and applied over the stored values. */
|
|
35
27
|
type LaunchPatch = Partial<Record<keyof LaunchOptions, unknown>>;
|
|
36
28
|
|
|
@@ -50,7 +42,6 @@ export function llamacppConsole(host: ProviderConsoleHost): Partial<ConsolePageC
|
|
|
50
42
|
return found.entry;
|
|
51
43
|
};
|
|
52
44
|
return {
|
|
53
|
-
config: [],
|
|
54
45
|
panels: [
|
|
55
46
|
{ id: 'runtime', title: S.runtimePanel, description: S.runtimePanelDescription, slot: 'instance' },
|
|
56
47
|
{ id: 'models', title: S.modelsPanel, description: S.modelsPanelDescription, slot: 'instance' },
|
|
@@ -59,17 +50,7 @@ export function llamacppConsole(host: ProviderConsoleHost): Partial<ConsolePageC
|
|
|
59
50
|
if (panel === 'runtime') {
|
|
60
51
|
const value = body(args);
|
|
61
52
|
const name = value.name as string;
|
|
62
|
-
if (method === 'state') {
|
|
63
|
-
const entry = entryOf(name);
|
|
64
|
-
const prefix = `providers.${name}.`;
|
|
65
|
-
const config = runtimeConfig(name, entry, host.language).map((group) => ({
|
|
66
|
-
group,
|
|
67
|
-
values: Object.fromEntries(Object.keys(group.schema.properties).map((path) => [
|
|
68
|
-
path, getByPath(entry as unknown as Record<string, unknown>, path.slice(prefix.length)) ?? '',
|
|
69
|
-
])) as ConfigValues,
|
|
70
|
-
}));
|
|
71
|
-
return { name, ...(await control(name).runtime.state(host.language)), config } satisfies RuntimePanelState;
|
|
72
|
-
}
|
|
53
|
+
if (method === 'state') return { name, ...(await control(name).runtime.state(host.language)) };
|
|
73
54
|
if (method === 'enable') {
|
|
74
55
|
const entry = entryOf(name);
|
|
75
56
|
const options = llamacppOptions(entry);
|
|
@@ -4,7 +4,6 @@ import type { Language } from '../../core/language.ts';
|
|
|
4
4
|
import { isContextOverflow } from '../transport/errors.ts';
|
|
5
5
|
import { modelsRoot, runtimesRoot } from '../../paths.ts';
|
|
6
6
|
import { RouterCatalog } from './catalog.ts';
|
|
7
|
-
import { runtimeConfig } from './config.ts';
|
|
8
7
|
import { llamacppConsole } from './console/server.ts';
|
|
9
8
|
import { LlamaCppProvider } from './native.ts';
|
|
10
9
|
import { backendChoices, llamacppOptions, normalizeLlamaCpp } from './options.ts';
|
|
@@ -28,7 +27,6 @@ export default {
|
|
|
28
27
|
baseUrlSuggestions: ['http://127.0.0.1:8090/v1', 'http://127.0.0.1:8080/v1'],
|
|
29
28
|
normalize: normalizeLlamaCpp,
|
|
30
29
|
console: llamacppConsole,
|
|
31
|
-
config: runtimeConfig,
|
|
32
30
|
reasoningTiers: reasoningTiers('zh'),
|
|
33
31
|
localize: (language) => ({ reasoningTiers: reasoningTiers(language) }),
|
|
34
32
|
serviceTiers: [],
|
|
@@ -42,8 +42,6 @@ export function normalizeLlamaCpp(entry: LLMProviderEntry): LLMProviderEntry {
|
|
|
42
42
|
if (options.runtime && typeof options.runtime === 'object') {
|
|
43
43
|
const runtime = options.runtime as Partial<RuntimeOptions>;
|
|
44
44
|
options.runtime = { ...runtime, release: runtime.release ?? PINNED_RELEASE, backend: runtime.backend ?? defaultBackend() };
|
|
45
|
-
if (typeof options.runtime.release === 'string') options.runtime.release = options.runtime.release.trim();
|
|
46
|
-
if (typeof options.runtime.runtimeDir === 'string') options.runtime.runtimeDir = options.runtime.runtimeDir.trim();
|
|
47
45
|
if (options.runtime.runtimeDir === '') delete options.runtime.runtimeDir;
|
|
48
46
|
options.launch = { ...LAUNCH_DEFAULTS, ...(options.launch ?? {}) };
|
|
49
47
|
}
|
|
@@ -101,9 +101,9 @@ const panelZh = {
|
|
|
101
101
|
'启动失败时可在 Windows 安全中心核对拦截记录;若确认被拦截,请使用符合信任要求的运行时。',
|
|
102
102
|
server: '服务进程',
|
|
103
103
|
serverSection: '服务进程与端点',
|
|
104
|
-
serverSectionDesc: '',
|
|
104
|
+
serverSectionDesc: 'llama-server 本地服务管理与监听状态。',
|
|
105
105
|
runtimeSection: '二进制运行环境',
|
|
106
|
-
runtimeSectionDesc: '',
|
|
106
|
+
runtimeSectionDesc: '官方 llama.cpp 二进制包与运行依赖管理。',
|
|
107
107
|
releaseHint: '上游 release tag,如 b10930;换了要重新下载。',
|
|
108
108
|
backendHint: '按本机平台给出的官方构建;CUDA 版自动搭配上游的 cudart 包。',
|
|
109
109
|
runtimeDirHint: '填了就不下载:目录里要有 llama-server 及其动态库(自编译、Linux CUDA、签过名的构建)。',
|
|
@@ -141,7 +141,7 @@ const panelZh = {
|
|
|
141
141
|
pullPlaceholder: 'HuggingFace 仓库,如 ggml-org/Qwen2.5-Coder-7B-Instruct-GGUF',
|
|
142
142
|
localDir: '本机 GGUF 目录',
|
|
143
143
|
cacheDir: '缓存目录',
|
|
144
|
-
noModels: '
|
|
144
|
+
noModels: '暂无可用的 GGUF 模型:可通过上方输入拉取,或将 .gguf 文件放入本机目录后点击重扫。',
|
|
145
145
|
modelStatusLabel: {
|
|
146
146
|
loaded: '已加载',
|
|
147
147
|
loading: '加载中',
|
|
@@ -175,9 +175,9 @@ const panelEn: typeof panelZh = {
|
|
|
175
175
|
'If startup fails, check Windows Security for a block record. If a block is confirmed, use a runtime that meets the trust requirements.',
|
|
176
176
|
server: 'Server',
|
|
177
177
|
serverSection: 'Server Process & Endpoint',
|
|
178
|
-
serverSectionDesc: '',
|
|
178
|
+
serverSectionDesc: 'llama-server local process and listening status.',
|
|
179
179
|
runtimeSection: 'Binary Runtime Environment',
|
|
180
|
-
runtimeSectionDesc: '',
|
|
180
|
+
runtimeSectionDesc: 'Official llama.cpp binary and dependencies.',
|
|
181
181
|
releaseHint: 'Upstream release tag, such as b10930; changing it downloads another build.',
|
|
182
182
|
backendHint: 'Official builds for this platform; CUDA builds are paired with the upstream cudart package.',
|
|
183
183
|
runtimeDirHint: 'When set nothing is downloaded: the directory holds llama-server and its shared libraries (self-built, Linux CUDA, signed builds).',
|
|
@@ -215,7 +215,7 @@ const panelEn: typeof panelZh = {
|
|
|
215
215
|
pullPlaceholder: 'HuggingFace repo, e.g. ggml-org/Qwen2.5-Coder-7B-Instruct-GGUF',
|
|
216
216
|
localDir: 'Local GGUF directory',
|
|
217
217
|
cacheDir: 'Cache directory',
|
|
218
|
-
noModels: 'No
|
|
218
|
+
noModels: 'No models yet: pull one above, or drop GGUF files into the local directory and rescan.',
|
|
219
219
|
modelStatusLabel: {
|
|
220
220
|
loaded: 'loaded',
|
|
221
221
|
loading: 'loading',
|
|
@@ -3,7 +3,6 @@ import type { LLMProviderEntry } from '../../core/types.ts';
|
|
|
3
3
|
import { isContextOverflow } from '../transport/errors.ts';
|
|
4
4
|
import { ModelCatalog, ResponsesProvider } from './native.ts';
|
|
5
5
|
import { text } from './strings.ts';
|
|
6
|
-
import { protocolConfig } from './config.ts';
|
|
7
6
|
|
|
8
7
|
/** Suggested base URLs for the console field; operators may enter other URLs. */
|
|
9
8
|
const BASE_URLS: readonly string[] = [
|
|
@@ -46,8 +45,6 @@ export default {
|
|
|
46
45
|
defaultBaseUrl: BASE_URLS[0],
|
|
47
46
|
baseUrlSuggestions: BASE_URLS,
|
|
48
47
|
normalize: normalizeCompat,
|
|
49
|
-
config: protocolConfig,
|
|
50
|
-
console: () => ({ config: [] }),
|
|
51
48
|
reasoningTiers: [],
|
|
52
49
|
effortSuggestions: EFFORTS,
|
|
53
50
|
serviceTiers: [],
|
package/src/web/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Owner: src/web/server.ts, src/web/shared/console-protocol.ts, src/web/shared/client-panel.ts
|
|
1
|
+
<!-- Owner: src/web/server.ts, src/web/shared/console-protocol.ts, src/web/shared/client-panel.ts -->
|
|
2
2
|
|
|
3
3
|
# src/web
|
|
4
4
|
|
|
@@ -50,13 +50,9 @@ manifest 的 `CONSOLE_PROTOCOL_VERSION` 不匹配时,浏览器拒绝加载。
|
|
|
50
50
|
扩展面板通过 `/assets/extensions/<包>/<版本>/<文件>` 提供,仅允许 manifest 声明的脚本与样式文件。
|
|
51
51
|
|
|
52
52
|
`createBot()` 通过 `WebAppDeps` 注入事件库、session、运行控制、配置、存储、`consolePageSources` 和扩展信息。
|
|
53
|
-
调试流以 `head` 提供本次 `Persona.sessionHead()` 的合成开头;它不属于持久 session。
|
|
54
|
-
`providersLamp` 汇总本地端点可用性,由状态灯接口提供给浏览器。
|
|
55
53
|
|
|
56
54
|
## 浏览器
|
|
57
55
|
|
|
58
|
-
`llm-settings` 从端点状态中的 ConfigGroup 渲染连接字段与声明的请求路径,使用 `ctx.setConfig` 保存。
|
|
59
|
-
|
|
60
56
|
`main.ts` 的 `FEATURES` 包含 `live`、`core`、`usage`、`provider`、`world`、`extensions`、`prompts`、
|
|
61
57
|
`appearance`、`settings`。贡献页由 manifest 加载,保留路由段 `provider` 交由 `ConsolePageHost` 处理。
|
|
62
58
|
`features/config/view.ts` 与 `features/storage/view.ts` 是配置组与存储清单的通用视图,运行诊断页与
|