@relay-harness/coding-agent 1.0.2 → 1.0.3
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/CHANGELOG.md +17 -6
- package/LICENSE +22 -0
- package/README.md +12 -13
- package/dist/bundle/chunks/{chunk-XSLAMYCK.js → chunk-5H4IRYOE.js} +555 -61
- package/dist/bundle/chunks/{chunk-H6S6DQTM.js → chunk-NS67SA75.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-FNVEVB65.js → virtual-modules-ZGB2J7DY.js} +1 -1
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/settings-manager.d.ts +5 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/extensions/laya/assessment.d.ts +7 -1
- package/dist/extensions/laya/assessment.d.ts.map +1 -1
- package/dist/extensions/laya/assessment.js +21 -1
- package/dist/extensions/laya/assessment.js.map +1 -1
- package/dist/extensions/laya/docker.d.ts +19 -0
- package/dist/extensions/laya/docker.d.ts.map +1 -0
- package/dist/extensions/laya/docker.js +44 -0
- package/dist/extensions/laya/docker.js.map +1 -0
- package/dist/extensions/laya/index.d.ts +12 -3
- package/dist/extensions/laya/index.d.ts.map +1 -1
- package/dist/extensions/laya/index.js +331 -67
- package/dist/extensions/laya/index.js.map +1 -1
- package/dist/extensions/laya/learn.d.ts +67 -0
- package/dist/extensions/laya/learn.d.ts.map +1 -0
- package/dist/extensions/laya/learn.js +263 -0
- package/dist/extensions/laya/learn.js.map +1 -0
- package/dist/extensions/laya/memory.d.ts +48 -0
- package/dist/extensions/laya/memory.d.ts.map +1 -0
- package/dist/extensions/laya/memory.js +107 -0
- package/dist/extensions/laya/memory.js.map +1 -0
- package/dist/extensions/laya/model-manifest.d.ts +1 -1
- package/dist/extensions/laya/model-manifest.js +1 -1
- package/dist/extensions/laya/model-manifest.js.map +1 -1
- package/dist/extensions/laya/routers.d.ts +8 -0
- package/dist/extensions/laya/routers.d.ts.map +1 -1
- package/dist/extensions/laya/routers.js +2 -0
- package/dist/extensions/laya/routers.js.map +1 -1
- package/dist/extensions/laya/runtime.d.ts +39 -49
- package/dist/extensions/laya/runtime.d.ts.map +1 -1
- package/dist/extensions/laya/runtime.js +93 -145
- package/dist/extensions/laya/runtime.js.map +1 -1
- package/dist/extensions/laya/server.d.ts +39 -15
- package/dist/extensions/laya/server.d.ts.map +1 -1
- package/dist/extensions/laya/server.js +212 -78
- package/dist/extensions/laya/server.js.map +1 -1
- package/dist/extensions/laya/train-script.d.ts +8 -0
- package/dist/extensions/laya/train-script.d.ts.map +1 -0
- package/dist/extensions/laya/train-script.js +422 -0
- package/dist/extensions/laya/train-script.js.map +1 -0
- package/dist/extensions/laya/training.d.ts +176 -0
- package/dist/extensions/laya/training.d.ts.map +1 -0
- package/dist/extensions/laya/training.js +337 -0
- package/dist/extensions/laya/training.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +3 -2
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -4
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +10 -6
- package/dist/utils/version-check.js.map +1 -1
- package/docs/laya.md +59 -19
- package/docs/quickstart.md +19 -6
- package/docs/settings.md +4 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +15 -11
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { runtimeStatus } from "./runtime.js";
|
|
1
|
+
import { DOCKER_MISSING, dockerError } from "./docker.js";
|
|
2
|
+
import { LAYA_QUESTIONS } from "./questions.js";
|
|
3
|
+
import { LAYA_CONTAINER, LAYA_CONTAINER_PORT, LAYA_IMAGE_PATHS, LAYA_IMAGE_REPOSITORY, LAYA_MODELS_VOLUME, } from "./runtime.js";
|
|
5
4
|
const HEALTH_TIMEOUT_MS = 1500;
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
5
|
+
/** Loading the model on the CPU of a slow machine takes a while. */
|
|
6
|
+
const START_TIMEOUT_MS = 180_000;
|
|
7
|
+
const POLL_MS = 1000;
|
|
8
|
+
const PULL_TIMEOUT_MS = 60 * 60_000;
|
|
9
|
+
const COMMAND_TIMEOUT_MS = 60_000;
|
|
10
|
+
/** Labels that describe what a server container runs; a container whose labels differ is replaced. */
|
|
11
|
+
const LABEL_IMAGE = "relay.laya.image";
|
|
12
|
+
const LABEL_MODEL = "relay.laya.model";
|
|
13
|
+
const LABEL_ADDRESS = "relay.laya.address";
|
|
14
|
+
/** Whether a URL points at this machine, the only place Relay runs a server. */
|
|
9
15
|
export function isLocalUrl(url) {
|
|
10
16
|
try {
|
|
11
17
|
return ["127.0.0.1", "localhost", "[::1]"].includes(new URL(url).hostname);
|
|
@@ -14,104 +20,232 @@ export function isLocalUrl(url) {
|
|
|
14
20
|
return false;
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
|
-
/**
|
|
23
|
+
/** Host address Docker publishes the server port on, from the configured URL. */
|
|
24
|
+
function publishAddress(baseUrl) {
|
|
25
|
+
const url = new URL(baseUrl);
|
|
26
|
+
const host = url.hostname === "[::1]" ? "[::1]" : "127.0.0.1";
|
|
27
|
+
return `${host}:${url.port || "80"}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The Laya server container. `install()` pulls the image when it is missing and keeps one container
|
|
31
|
+
* running with the active model, published on the configured local port only. The container
|
|
32
|
+
* restarts with Docker, so later sessions find it answering. A server that answers without being
|
|
33
|
+
* ours (no container named relay-laya) is used as is, unless it serves other questions than Relay's.
|
|
34
|
+
*/
|
|
18
35
|
export class LayaServer {
|
|
19
36
|
options;
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
installing;
|
|
38
|
+
state = "unknown";
|
|
39
|
+
/** Why the server is not ready, for `/laya status`. */
|
|
40
|
+
detail;
|
|
41
|
+
/** Image of the running container. */
|
|
42
|
+
image;
|
|
22
43
|
constructor(options) {
|
|
23
44
|
this.options = options;
|
|
24
45
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
async isUp() {
|
|
46
|
+
/** The server's health answer, when it answers. */
|
|
47
|
+
async health() {
|
|
29
48
|
const request = this.options.fetch ?? globalThis.fetch;
|
|
30
49
|
try {
|
|
31
50
|
const response = await request(new URL("/health", this.options.baseUrl()), {
|
|
32
51
|
signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),
|
|
33
52
|
});
|
|
34
|
-
|
|
53
|
+
if (!response.ok)
|
|
54
|
+
return undefined;
|
|
55
|
+
const body = (await response.json());
|
|
56
|
+
return body.ok === true ? body : undefined;
|
|
35
57
|
}
|
|
36
58
|
catch {
|
|
37
|
-
return
|
|
59
|
+
return undefined;
|
|
38
60
|
}
|
|
39
61
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
async isUp() {
|
|
63
|
+
return (await this.health()) !== undefined;
|
|
64
|
+
}
|
|
65
|
+
/** Makes the server answer: pulls, creates, replaces or starts the container. Concurrent calls share one run. */
|
|
66
|
+
install(report = () => { }, signal) {
|
|
67
|
+
this.installing ??= this.run(report, signal).finally(() => {
|
|
68
|
+
this.installing = undefined;
|
|
44
69
|
});
|
|
45
|
-
return this.
|
|
70
|
+
return this.installing;
|
|
46
71
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
fail(state, detail) {
|
|
73
|
+
this.state = state;
|
|
74
|
+
this.detail = detail;
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
async run(report, signal) {
|
|
78
|
+
const { docker } = this.options;
|
|
79
|
+
const baseUrl = this.options.baseUrl();
|
|
80
|
+
if (!isLocalUrl(baseUrl)) {
|
|
81
|
+
// A remote server is not Relay's to manage.
|
|
82
|
+
const up = await this.isUp();
|
|
83
|
+
return up ? this.ready() : this.fail("failed", `${baseUrl} does not answer`);
|
|
84
|
+
}
|
|
85
|
+
this.state = "installing";
|
|
86
|
+
this.detail = undefined;
|
|
87
|
+
const version = await docker(["version", "--format", "{{.Server.Version}}"], {
|
|
88
|
+
signal,
|
|
89
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
90
|
+
});
|
|
91
|
+
if (version.code !== 0) {
|
|
92
|
+
return this.fail("no-docker", version.code === DOCKER_MISSING
|
|
93
|
+
? "Docker is not installed"
|
|
94
|
+
: `Docker does not answer; start Docker Desktop or the Docker service (${dockerError(version)})`);
|
|
95
|
+
}
|
|
96
|
+
const container = await this.inspect(signal);
|
|
97
|
+
const foreign = container ? undefined : await this.health();
|
|
98
|
+
if (foreign) {
|
|
99
|
+
// Someone else's server on this port: use it, unless it says it answers other questions,
|
|
100
|
+
// like a laya-trainer server of another project.
|
|
101
|
+
const decisions = Array.isArray(foreign.decisions) ? foreign.decisions : undefined;
|
|
102
|
+
if (decisions && !Object.keys(LAYA_QUESTIONS).every((name) => decisions.includes(name))) {
|
|
103
|
+
return this.fail("failed", `another Laya server, which answers other questions, uses ${publishAddress(baseUrl)}; stop it or set laya.baseUrl to a free port`);
|
|
104
|
+
}
|
|
105
|
+
return this.ready();
|
|
106
|
+
}
|
|
107
|
+
const image = await this.options.image();
|
|
108
|
+
const present = await docker(["image", "inspect", "--format", "{{.Id}}", image], { signal });
|
|
109
|
+
if (present.code !== 0) {
|
|
110
|
+
report("downloading the Laya image");
|
|
111
|
+
let layers = 0;
|
|
112
|
+
let done = 0;
|
|
113
|
+
const pull = await docker(["pull", image], {
|
|
114
|
+
signal,
|
|
115
|
+
timeout: PULL_TIMEOUT_MS,
|
|
116
|
+
onLine: (line) => {
|
|
117
|
+
if (/: Pulling fs layer$/.test(line))
|
|
118
|
+
layers++;
|
|
119
|
+
else if (/: (Pull complete|Already exists)$/.test(line))
|
|
120
|
+
done++;
|
|
121
|
+
else
|
|
122
|
+
return;
|
|
123
|
+
report(`downloading the Laya image: ${done}/${layers} layers`);
|
|
74
124
|
},
|
|
75
125
|
});
|
|
126
|
+
if (pull.code !== 0)
|
|
127
|
+
return this.fail("failed", `Pulling ${image} failed: ${dockerError(pull)}`);
|
|
76
128
|
}
|
|
77
|
-
|
|
78
|
-
|
|
129
|
+
const labels = {
|
|
130
|
+
[LABEL_IMAGE]: image,
|
|
131
|
+
[LABEL_MODEL]: this.options.model(),
|
|
132
|
+
[LABEL_ADDRESS]: publishAddress(baseUrl),
|
|
133
|
+
};
|
|
134
|
+
const matches = container && Object.entries(labels).every(([key, value]) => container.labels[key] === value);
|
|
135
|
+
if (container && !matches) {
|
|
136
|
+
const removed = await docker(["rm", "--force", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });
|
|
137
|
+
if (removed.code !== 0)
|
|
138
|
+
return this.fail("failed", `Replacing the Laya container failed: ${dockerError(removed)}`);
|
|
79
139
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
this.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
140
|
+
if (!matches) {
|
|
141
|
+
report("starting the Laya server");
|
|
142
|
+
const created = await docker(this.runArgs(image, labels), { signal, timeout: COMMAND_TIMEOUT_MS });
|
|
143
|
+
// Another Relay created it at the same time: use theirs, it runs the same image and model.
|
|
144
|
+
if (created.code !== 0 && !(await this.inspect(signal))) {
|
|
145
|
+
return this.fail("failed", `Starting the Laya container failed: ${dockerError(created)}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (container && !container.running) {
|
|
149
|
+
report("starting the Laya server");
|
|
150
|
+
const started = await docker(["start", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });
|
|
151
|
+
if (started.code !== 0)
|
|
152
|
+
return this.fail("failed", `Starting the Laya container failed: ${dockerError(started)}`);
|
|
153
|
+
}
|
|
154
|
+
// A running container that matches is only checked; nothing new to report.
|
|
155
|
+
if (!matches || !container?.running)
|
|
156
|
+
report("loading the Laya model");
|
|
90
157
|
const deadline = Date.now() + START_TIMEOUT_MS;
|
|
91
|
-
while (Date.now() < deadline && !
|
|
92
|
-
if (await this.isUp())
|
|
93
|
-
|
|
158
|
+
while (Date.now() < deadline && !signal?.aborted) {
|
|
159
|
+
if (await this.isUp()) {
|
|
160
|
+
this.image = image;
|
|
161
|
+
await this.removeOtherImages(image);
|
|
162
|
+
return this.ready();
|
|
163
|
+
}
|
|
164
|
+
const current = await this.inspect(signal);
|
|
165
|
+
if (!current?.running) {
|
|
166
|
+
const logs = await docker(["logs", "--tail", "5", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });
|
|
167
|
+
return this.fail("failed", `The Laya container stopped: ${dockerError(logs)}`);
|
|
168
|
+
}
|
|
94
169
|
await new Promise((resolve) => setTimeout(resolve, POLL_MS));
|
|
95
170
|
}
|
|
96
|
-
this.
|
|
97
|
-
return false;
|
|
171
|
+
return this.fail("failed", "The Laya server did not answer in time (see docker logs relay-laya)");
|
|
98
172
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
173
|
+
ready() {
|
|
174
|
+
this.state = "ready";
|
|
175
|
+
this.detail = undefined;
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
runArgs(image, labels) {
|
|
179
|
+
return [
|
|
180
|
+
"run",
|
|
181
|
+
"--detach",
|
|
182
|
+
"--name",
|
|
183
|
+
LAYA_CONTAINER,
|
|
184
|
+
"--restart",
|
|
185
|
+
"unless-stopped",
|
|
186
|
+
"--publish",
|
|
187
|
+
`${labels[LABEL_ADDRESS]}:${LAYA_CONTAINER_PORT}`,
|
|
188
|
+
"--volume",
|
|
189
|
+
`${LAYA_MODELS_VOLUME}:${LAYA_IMAGE_PATHS.trainedModels}`,
|
|
190
|
+
...Object.entries(labels).flatMap(([key, value]) => ["--label", `${key}=${value}`]),
|
|
191
|
+
image,
|
|
192
|
+
"python",
|
|
193
|
+
LAYA_IMAGE_PATHS.serveScript,
|
|
194
|
+
"--model",
|
|
195
|
+
labels[LABEL_MODEL],
|
|
196
|
+
"--host",
|
|
197
|
+
"0.0.0.0",
|
|
198
|
+
"--port",
|
|
199
|
+
String(LAYA_CONTAINER_PORT),
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
async inspect(signal) {
|
|
203
|
+
const result = await this.options.docker(["container", "inspect", "--format", "{{json .State.Running}} {{json .Config.Labels}}", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });
|
|
204
|
+
if (result.code !== 0)
|
|
205
|
+
return undefined;
|
|
206
|
+
const [running, ...labels] = result.stdout.trim().split(" ");
|
|
207
|
+
try {
|
|
208
|
+
return {
|
|
209
|
+
running: running === "true",
|
|
210
|
+
labels: JSON.parse(labels.join(" ")) ?? {},
|
|
211
|
+
};
|
|
108
212
|
}
|
|
109
|
-
|
|
110
|
-
|
|
213
|
+
catch {
|
|
214
|
+
return { running: running === "true", labels: {} };
|
|
111
215
|
}
|
|
112
216
|
}
|
|
113
|
-
|
|
114
|
-
|
|
217
|
+
/** Frees the disk taken by images of earlier Relay versions. */
|
|
218
|
+
async removeOtherImages(image) {
|
|
219
|
+
const listed = await this.options.docker(["image", "ls", LAYA_IMAGE_REPOSITORY, "--format", "{{.Repository}}:{{.Tag}}"], { timeout: COMMAND_TIMEOUT_MS });
|
|
220
|
+
if (listed.code !== 0)
|
|
221
|
+
return;
|
|
222
|
+
const others = listed.stdout
|
|
223
|
+
.split("\n")
|
|
224
|
+
.map((line) => line.trim())
|
|
225
|
+
.filter((ref) => ref && ref !== image && !ref.endsWith(":<none>"));
|
|
226
|
+
// An image still used by a training container is kept; rmi fails on it.
|
|
227
|
+
if (others.length > 0)
|
|
228
|
+
await this.options.docker(["rmi", ...others], { timeout: COMMAND_TIMEOUT_MS });
|
|
229
|
+
}
|
|
230
|
+
/** Stops the server container; the next install starts it again. */
|
|
231
|
+
async stop() {
|
|
232
|
+
await this.options.docker(["stop", LAYA_CONTAINER], { timeout: COMMAND_TIMEOUT_MS });
|
|
233
|
+
if (this.state === "ready")
|
|
234
|
+
this.state = "unknown";
|
|
235
|
+
}
|
|
236
|
+
describe() {
|
|
237
|
+
switch (this.state) {
|
|
238
|
+
case "ready":
|
|
239
|
+
return `running in Docker${this.image ? ` (${this.image}, model ${this.options.model()})` : ""}`;
|
|
240
|
+
case "installing":
|
|
241
|
+
return "installing in Docker";
|
|
242
|
+
case "no-docker":
|
|
243
|
+
return `not available: ${this.detail}`;
|
|
244
|
+
case "failed":
|
|
245
|
+
return `failed: ${this.detail}`;
|
|
246
|
+
default:
|
|
247
|
+
return "not checked yet";
|
|
248
|
+
}
|
|
115
249
|
}
|
|
116
250
|
}
|
|
117
251
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/extensions/laya/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAA0C,aAAa,EAAE,MAAM,cAAc,CAAC;AAErF,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACjC,MAAM,OAAO,GAAG,GAAG,CAAC;AAUpB,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,GAAW;IACrC,IAAI,CAAC;QACJ,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,0GAA0G;AAC1G,MAAM,OAAO,UAAU;IACL,OAAO,CAAoB;IACpC,KAAK,CAA2B;IAChC,QAAQ,CAA+B;IAE/C,YAAY,OAA0B;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;IAED,MAAM;QACL,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,IAAI;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QACvD,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE;gBAC1E,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC;aAC9C,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,EAAE,IAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC,EAAE,KAAK,IAAI,CAAC;QACjF,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED,qHAAqH;IACrH,aAAa,CAAC,MAAoB;QACjC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YACzD,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,MAAoB;QAC/C,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QACnC,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QACnF,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAC/B,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC;YACJ,IAAI,CAAC,KAAK,GAAG,KAAK,CACjB,KAAK,CAAC,UAAU,EAChB;gBACC,KAAK,CAAC,WAAW;gBACjB,SAAS;gBACT,KAAK,CAAC,KAAK;gBACX,QAAQ;gBACR,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;gBAChC,QAAQ;gBACR,IAAI,IAAI,MAAM;aACd,EACD;gBACC,KAAK,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;gBAC3B,WAAW,EAAE,IAAI;gBACjB,GAAG,EAAE;oBACJ,GAAG,OAAO,CAAC,GAAG;oBACd,MAAM,EAAE,GAAG;oBACX,cAAc,EAAE,GAAG;oBACnB,oBAAoB,EAAE,GAAG;oBACzB,gBAAgB,EAAE,OAAO;iBACzB;aACD,CACD,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,SAAS,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACvB,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;gBAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QAClD,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACxB,MAAM,GAAG,IAAI,CAAC;QACf,CAAC,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAC/C,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC7D,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE;gBAAE,OAAO,IAAI,CAAC;YACnC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACd,CAAC;IAED,6CAA6C;IAC7C,IAAI;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,KAAK,EAAE,GAAG;YAAE,OAAO;QACxB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClC,sEAAsE;YACtE,SAAS,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACvF,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;IACF,CAAC;IAED,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IACjC,CAAC;CACD","sourcesContent":["import { type ChildProcess, spawn, spawnSync } from \"node:child_process\";\nimport { closeSync, mkdirSync, openSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { type LayaModelManifest, type LayaPaths, runtimeStatus } from \"./runtime.ts\";\n\nconst HEALTH_TIMEOUT_MS = 1500;\nconst START_TIMEOUT_MS = 120_000;\nconst POLL_MS = 500;\n\nexport interface LayaServerOptions {\n\tpaths: LayaPaths;\n\tmanifest: LayaModelManifest;\n\t/** The configured System One URL, e.g. `http://127.0.0.1:8000/v1`. */\n\tbaseUrl: () => string;\n\tfetch?: typeof fetch;\n}\n\n/** Whether a URL points at this machine, the only place Relay starts a server. */\nexport function isLocalUrl(url: string): boolean {\n\ttry {\n\t\treturn [\"127.0.0.1\", \"localhost\", \"[::1]\"].includes(new URL(url).hostname);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** Starts and stops the local Laya server. A server that is already listening is used, never replaced. */\nexport class LayaServer {\n\tprivate readonly options: LayaServerOptions;\n\tprivate child: ChildProcess | undefined;\n\tprivate starting: Promise<boolean> | undefined;\n\n\tconstructor(options: LayaServerOptions) {\n\t\tthis.options = options;\n\t}\n\n\tstatus() {\n\t\treturn runtimeStatus(this.options.manifest, this.options.paths);\n\t}\n\n\tasync isUp(): Promise<boolean> {\n\t\tconst request = this.options.fetch ?? globalThis.fetch;\n\t\ttry {\n\t\t\tconst response = await request(new URL(\"/health\", this.options.baseUrl()), {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\treturn response.ok && ((await response.json()) as { ok?: boolean }).ok === true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** True when a server answers, starting ours first if the runtime is installed. Concurrent calls share one start. */\n\tensureRunning(signal?: AbortSignal): Promise<boolean> {\n\t\tthis.starting ??= this.startIfNeeded(signal).finally(() => {\n\t\t\tthis.starting = undefined;\n\t\t});\n\t\treturn this.starting;\n\t}\n\n\tprivate async startIfNeeded(signal?: AbortSignal): Promise<boolean> {\n\t\tif (await this.isUp()) return true;\n\t\tif (this.status() !== \"ready\" || !isLocalUrl(this.options.baseUrl())) return false;\n\t\tconst { hostname, port } = new URL(this.options.baseUrl());\n\t\tconst { paths } = this.options;\n\t\tmkdirSync(dirname(paths.log), { recursive: true });\n\t\tconst log = openSync(paths.log, \"a\");\n\t\ttry {\n\t\t\tthis.child = spawn(\n\t\t\t\tpaths.venvPython,\n\t\t\t\t[\n\t\t\t\t\tpaths.serveScript,\n\t\t\t\t\t\"--model\",\n\t\t\t\t\tpaths.model,\n\t\t\t\t\t\"--host\",\n\t\t\t\t\thostname.replace(/^\\[|\\]$/g, \"\"),\n\t\t\t\t\t\"--port\",\n\t\t\t\t\tport || \"8000\",\n\t\t\t\t],\n\t\t\t\t{\n\t\t\t\t\tstdio: [\"ignore\", log, log],\n\t\t\t\t\twindowsHide: true,\n\t\t\t\t\tenv: {\n\t\t\t\t\t\t...process.env,\n\t\t\t\t\t\tUSE_TF: \"0\",\n\t\t\t\t\t\tHF_HUB_OFFLINE: \"1\",\n\t\t\t\t\t\tTRANSFORMERS_OFFLINE: \"1\",\n\t\t\t\t\t\tPYTHONIOENCODING: \"utf-8\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} finally {\n\t\t\tcloseSync(log);\n\t\t}\n\t\tconst child = this.child;\n\t\tlet exited = false;\n\t\tchild.once(\"exit\", () => {\n\t\t\texited = true;\n\t\t\tif (this.child === child) this.child = undefined;\n\t\t});\n\t\tchild.once(\"error\", () => {\n\t\t\texited = true;\n\t\t});\n\t\tconst deadline = Date.now() + START_TIMEOUT_MS;\n\t\twhile (Date.now() < deadline && !exited && !signal?.aborted) {\n\t\t\tif (await this.isUp()) return true;\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, POLL_MS));\n\t\t}\n\t\tthis.stop();\n\t\treturn false;\n\t}\n\n\t/** Stops the server this process started. */\n\tstop(): void {\n\t\tconst child = this.child;\n\t\tthis.child = undefined;\n\t\tif (!child?.pid) return;\n\t\tif (process.platform === \"win32\") {\n\t\t\t// The venv launcher spawns the real interpreter; kill the whole tree.\n\t\t\tspawnSync(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], { windowsHide: true });\n\t\t} else {\n\t\t\tchild.kill();\n\t\t}\n\t}\n\n\tget running(): boolean {\n\t\treturn this.child !== undefined;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/extensions/laya/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { DOCKER_MISSING, type DockerRun, dockerError } from \"./docker.ts\";\nimport { LAYA_QUESTIONS } from \"./questions.ts\";\nimport {\n\tLAYA_CONTAINER,\n\tLAYA_CONTAINER_PORT,\n\tLAYA_IMAGE_PATHS,\n\tLAYA_IMAGE_REPOSITORY,\n\tLAYA_MODELS_VOLUME,\n} from \"./runtime.ts\";\n\nconst HEALTH_TIMEOUT_MS = 1500;\n/** Loading the model on the CPU of a slow machine takes a while. */\nconst START_TIMEOUT_MS = 180_000;\nconst POLL_MS = 1000;\nconst PULL_TIMEOUT_MS = 60 * 60_000;\nconst COMMAND_TIMEOUT_MS = 60_000;\n\n/** Labels that describe what a server container runs; a container whose labels differ is replaced. */\nconst LABEL_IMAGE = \"relay.laya.image\";\nconst LABEL_MODEL = \"relay.laya.model\";\nconst LABEL_ADDRESS = \"relay.laya.address\";\n\nexport type LayaServerState =\n\t/** Not checked yet in this process. */\n\t| \"unknown\"\n\t/** The docker command is missing or its daemon does not answer. */\n\t| \"no-docker\"\n\t| \"installing\"\n\t| \"ready\"\n\t| \"failed\";\n\nexport interface LayaServerOptions {\n\tdocker: DockerRun;\n\t/** The configured System One URL, e.g. `http://127.0.0.1:8737/v1`. */\n\tbaseUrl: () => string;\n\t/** Image to run. */\n\timage: () => Promise<string>;\n\t/** Model to serve, as a path inside the container. */\n\tmodel: () => string;\n\tfetch?: typeof fetch;\n}\n\n/** Whether a URL points at this machine, the only place Relay runs a server. */\nexport function isLocalUrl(url: string): boolean {\n\ttry {\n\t\treturn [\"127.0.0.1\", \"localhost\", \"[::1]\"].includes(new URL(url).hostname);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** Host address Docker publishes the server port on, from the configured URL. */\nfunction publishAddress(baseUrl: string): string {\n\tconst url = new URL(baseUrl);\n\tconst host = url.hostname === \"[::1]\" ? \"[::1]\" : \"127.0.0.1\";\n\treturn `${host}:${url.port || \"80\"}`;\n}\n\n/**\n * The Laya server container. `install()` pulls the image when it is missing and keeps one container\n * running with the active model, published on the configured local port only. The container\n * restarts with Docker, so later sessions find it answering. A server that answers without being\n * ours (no container named relay-laya) is used as is, unless it serves other questions than Relay's.\n */\nexport class LayaServer {\n\tprivate readonly options: LayaServerOptions;\n\tprivate installing: Promise<boolean> | undefined;\n\tstate: LayaServerState = \"unknown\";\n\t/** Why the server is not ready, for `/laya status`. */\n\tdetail: string | undefined;\n\t/** Image of the running container. */\n\timage: string | undefined;\n\n\tconstructor(options: LayaServerOptions) {\n\t\tthis.options = options;\n\t}\n\n\t/** The server's health answer, when it answers. */\n\tprivate async health(): Promise<{ ok?: boolean; decisions?: unknown } | undefined> {\n\t\tconst request = this.options.fetch ?? globalThis.fetch;\n\t\ttry {\n\t\t\tconst response = await request(new URL(\"/health\", this.options.baseUrl()), {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\tif (!response.ok) return undefined;\n\t\t\tconst body = (await response.json()) as { ok?: boolean; decisions?: unknown };\n\t\t\treturn body.ok === true ? body : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tasync isUp(): Promise<boolean> {\n\t\treturn (await this.health()) !== undefined;\n\t}\n\n\t/** Makes the server answer: pulls, creates, replaces or starts the container. Concurrent calls share one run. */\n\tinstall(report: (message: string) => void = () => {}, signal?: AbortSignal): Promise<boolean> {\n\t\tthis.installing ??= this.run(report, signal).finally(() => {\n\t\t\tthis.installing = undefined;\n\t\t});\n\t\treturn this.installing;\n\t}\n\n\tprivate fail(state: \"no-docker\" | \"failed\", detail: string): false {\n\t\tthis.state = state;\n\t\tthis.detail = detail;\n\t\treturn false;\n\t}\n\n\tprivate async run(report: (message: string) => void, signal?: AbortSignal): Promise<boolean> {\n\t\tconst { docker } = this.options;\n\t\tconst baseUrl = this.options.baseUrl();\n\t\tif (!isLocalUrl(baseUrl)) {\n\t\t\t// A remote server is not Relay's to manage.\n\t\t\tconst up = await this.isUp();\n\t\t\treturn up ? this.ready() : this.fail(\"failed\", `${baseUrl} does not answer`);\n\t\t}\n\t\tthis.state = \"installing\";\n\t\tthis.detail = undefined;\n\n\t\tconst version = await docker([\"version\", \"--format\", \"{{.Server.Version}}\"], {\n\t\t\tsignal,\n\t\t\ttimeout: COMMAND_TIMEOUT_MS,\n\t\t});\n\t\tif (version.code !== 0) {\n\t\t\treturn this.fail(\n\t\t\t\t\"no-docker\",\n\t\t\t\tversion.code === DOCKER_MISSING\n\t\t\t\t\t? \"Docker is not installed\"\n\t\t\t\t\t: `Docker does not answer; start Docker Desktop or the Docker service (${dockerError(version)})`,\n\t\t\t);\n\t\t}\n\n\t\tconst container = await this.inspect(signal);\n\t\tconst foreign = container ? undefined : await this.health();\n\t\tif (foreign) {\n\t\t\t// Someone else's server on this port: use it, unless it says it answers other questions,\n\t\t\t// like a laya-trainer server of another project.\n\t\t\tconst decisions = Array.isArray(foreign.decisions) ? foreign.decisions : undefined;\n\t\t\tif (decisions && !Object.keys(LAYA_QUESTIONS).every((name) => decisions.includes(name))) {\n\t\t\t\treturn this.fail(\n\t\t\t\t\t\"failed\",\n\t\t\t\t\t`another Laya server, which answers other questions, uses ${publishAddress(baseUrl)}; stop it or set laya.baseUrl to a free port`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.ready();\n\t\t}\n\n\t\tconst image = await this.options.image();\n\t\tconst present = await docker([\"image\", \"inspect\", \"--format\", \"{{.Id}}\", image], { signal });\n\t\tif (present.code !== 0) {\n\t\t\treport(\"downloading the Laya image\");\n\t\t\tlet layers = 0;\n\t\t\tlet done = 0;\n\t\t\tconst pull = await docker([\"pull\", image], {\n\t\t\t\tsignal,\n\t\t\t\ttimeout: PULL_TIMEOUT_MS,\n\t\t\t\tonLine: (line) => {\n\t\t\t\t\tif (/: Pulling fs layer$/.test(line)) layers++;\n\t\t\t\t\telse if (/: (Pull complete|Already exists)$/.test(line)) done++;\n\t\t\t\t\telse return;\n\t\t\t\t\treport(`downloading the Laya image: ${done}/${layers} layers`);\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (pull.code !== 0) return this.fail(\"failed\", `Pulling ${image} failed: ${dockerError(pull)}`);\n\t\t}\n\n\t\tconst labels = {\n\t\t\t[LABEL_IMAGE]: image,\n\t\t\t[LABEL_MODEL]: this.options.model(),\n\t\t\t[LABEL_ADDRESS]: publishAddress(baseUrl),\n\t\t};\n\t\tconst matches = container && Object.entries(labels).every(([key, value]) => container.labels[key] === value);\n\t\tif (container && !matches) {\n\t\t\tconst removed = await docker([\"rm\", \"--force\", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });\n\t\t\tif (removed.code !== 0)\n\t\t\t\treturn this.fail(\"failed\", `Replacing the Laya container failed: ${dockerError(removed)}`);\n\t\t}\n\t\tif (!matches) {\n\t\t\treport(\"starting the Laya server\");\n\t\t\tconst created = await docker(this.runArgs(image, labels), { signal, timeout: COMMAND_TIMEOUT_MS });\n\t\t\t// Another Relay created it at the same time: use theirs, it runs the same image and model.\n\t\t\tif (created.code !== 0 && !(await this.inspect(signal))) {\n\t\t\t\treturn this.fail(\"failed\", `Starting the Laya container failed: ${dockerError(created)}`);\n\t\t\t}\n\t\t} else if (container && !container.running) {\n\t\t\treport(\"starting the Laya server\");\n\t\t\tconst started = await docker([\"start\", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });\n\t\t\tif (started.code !== 0)\n\t\t\t\treturn this.fail(\"failed\", `Starting the Laya container failed: ${dockerError(started)}`);\n\t\t}\n\n\t\t// A running container that matches is only checked; nothing new to report.\n\t\tif (!matches || !container?.running) report(\"loading the Laya model\");\n\t\tconst deadline = Date.now() + START_TIMEOUT_MS;\n\t\twhile (Date.now() < deadline && !signal?.aborted) {\n\t\t\tif (await this.isUp()) {\n\t\t\t\tthis.image = image;\n\t\t\t\tawait this.removeOtherImages(image);\n\t\t\t\treturn this.ready();\n\t\t\t}\n\t\t\tconst current = await this.inspect(signal);\n\t\t\tif (!current?.running) {\n\t\t\t\tconst logs = await docker([\"logs\", \"--tail\", \"5\", LAYA_CONTAINER], { signal, timeout: COMMAND_TIMEOUT_MS });\n\t\t\t\treturn this.fail(\"failed\", `The Laya container stopped: ${dockerError(logs)}`);\n\t\t\t}\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, POLL_MS));\n\t\t}\n\t\treturn this.fail(\"failed\", \"The Laya server did not answer in time (see docker logs relay-laya)\");\n\t}\n\n\tprivate ready(): true {\n\t\tthis.state = \"ready\";\n\t\tthis.detail = undefined;\n\t\treturn true;\n\t}\n\n\tprivate runArgs(image: string, labels: Record<string, string>): string[] {\n\t\treturn [\n\t\t\t\"run\",\n\t\t\t\"--detach\",\n\t\t\t\"--name\",\n\t\t\tLAYA_CONTAINER,\n\t\t\t\"--restart\",\n\t\t\t\"unless-stopped\",\n\t\t\t\"--publish\",\n\t\t\t`${labels[LABEL_ADDRESS]}:${LAYA_CONTAINER_PORT}`,\n\t\t\t\"--volume\",\n\t\t\t`${LAYA_MODELS_VOLUME}:${LAYA_IMAGE_PATHS.trainedModels}`,\n\t\t\t...Object.entries(labels).flatMap(([key, value]) => [\"--label\", `${key}=${value}`]),\n\t\t\timage,\n\t\t\t\"python\",\n\t\t\tLAYA_IMAGE_PATHS.serveScript,\n\t\t\t\"--model\",\n\t\t\tlabels[LABEL_MODEL],\n\t\t\t\"--host\",\n\t\t\t\"0.0.0.0\",\n\t\t\t\"--port\",\n\t\t\tString(LAYA_CONTAINER_PORT),\n\t\t];\n\t}\n\n\tprivate async inspect(\n\t\tsignal?: AbortSignal,\n\t): Promise<{ running: boolean; labels: Record<string, string> } | undefined> {\n\t\tconst result = await this.options.docker(\n\t\t\t[\"container\", \"inspect\", \"--format\", \"{{json .State.Running}} {{json .Config.Labels}}\", LAYA_CONTAINER],\n\t\t\t{ signal, timeout: COMMAND_TIMEOUT_MS },\n\t\t);\n\t\tif (result.code !== 0) return undefined;\n\t\tconst [running, ...labels] = result.stdout.trim().split(\" \");\n\t\ttry {\n\t\t\treturn {\n\t\t\t\trunning: running === \"true\",\n\t\t\t\tlabels: (JSON.parse(labels.join(\" \")) as Record<string, string> | null) ?? {},\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { running: running === \"true\", labels: {} };\n\t\t}\n\t}\n\n\t/** Frees the disk taken by images of earlier Relay versions. */\n\tprivate async removeOtherImages(image: string): Promise<void> {\n\t\tconst listed = await this.options.docker(\n\t\t\t[\"image\", \"ls\", LAYA_IMAGE_REPOSITORY, \"--format\", \"{{.Repository}}:{{.Tag}}\"],\n\t\t\t{ timeout: COMMAND_TIMEOUT_MS },\n\t\t);\n\t\tif (listed.code !== 0) return;\n\t\tconst others = listed.stdout\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter((ref) => ref && ref !== image && !ref.endsWith(\":<none>\"));\n\t\t// An image still used by a training container is kept; rmi fails on it.\n\t\tif (others.length > 0) await this.options.docker([\"rmi\", ...others], { timeout: COMMAND_TIMEOUT_MS });\n\t}\n\n\t/** Stops the server container; the next install starts it again. */\n\tasync stop(): Promise<void> {\n\t\tawait this.options.docker([\"stop\", LAYA_CONTAINER], { timeout: COMMAND_TIMEOUT_MS });\n\t\tif (this.state === \"ready\") this.state = \"unknown\";\n\t}\n\n\tdescribe(): string {\n\t\tswitch (this.state) {\n\t\t\tcase \"ready\":\n\t\t\t\treturn `running in Docker${this.image ? ` (${this.image}, model ${this.options.model()})` : \"\"}`;\n\t\t\tcase \"installing\":\n\t\t\t\treturn \"installing in Docker\";\n\t\t\tcase \"no-docker\":\n\t\t\t\treturn `not available: ${this.detail}`;\n\t\t\tcase \"failed\":\n\t\t\t\treturn `failed: ${this.detail}`;\n\t\t\tdefault:\n\t\t\t\treturn \"not checked yet\";\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Training script for the routing model. It is built into the Laya Docker image as `train.py`, and
|
|
3
|
+
* `LayaTrainer` runs it in a training container. It is adapted from laya-trainer's
|
|
4
|
+
* `laya_ml.py`; the recipe and the hash split match, so its test split is the one the shipped model
|
|
5
|
+
* never saw.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TRAIN_SCRIPT: string;
|
|
8
|
+
//# sourceMappingURL=train-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"train-script.d.ts","sourceRoot":"","sources":["../../../src/extensions/laya/train-script.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,YAAY,QA8ZxB,CAAC"}
|