@workglow/ai 0.0.57 → 0.0.59
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 +44 -9
- package/dist/browser.js +89 -104
- package/dist/browser.js.map +13 -13
- package/dist/bun.js +89 -130
- package/dist/bun.js.map +13 -15
- package/dist/common.d.ts +2 -1
- package/dist/common.d.ts.map +1 -1
- package/dist/model/{storage/InMemoryModelRepository.d.ts → InMemoryModelRepository.d.ts} +2 -2
- package/dist/model/InMemoryModelRepository.d.ts.map +1 -0
- package/dist/model/ModelRegistry.d.ts +12 -0
- package/dist/model/ModelRegistry.d.ts.map +1 -1
- package/dist/model/ModelRepository.d.ts +59 -60
- package/dist/model/ModelRepository.d.ts.map +1 -1
- package/dist/model/ModelSchema.d.ts +42 -0
- package/dist/model/ModelSchema.d.ts.map +1 -0
- package/dist/node.js +89 -130
- package/dist/node.js.map +13 -15
- package/dist/provider/AiProviderRegistry.d.ts +15 -3
- package/dist/provider/AiProviderRegistry.d.ts.map +1 -1
- package/dist/task/DownloadModelTask.d.ts +179 -19
- package/dist/task/DownloadModelTask.d.ts.map +1 -1
- package/dist/task/TextEmbeddingTask.d.ts +77 -9
- package/dist/task/TextEmbeddingTask.d.ts.map +1 -1
- package/dist/task/TextGenerationTask.d.ts +77 -9
- package/dist/task/TextGenerationTask.d.ts.map +1 -1
- package/dist/task/TextQuestionAnswerTask.d.ts +77 -9
- package/dist/task/TextQuestionAnswerTask.d.ts.map +1 -1
- package/dist/task/TextRewriterTask.d.ts +77 -9
- package/dist/task/TextRewriterTask.d.ts.map +1 -1
- package/dist/task/TextSummaryTask.d.ts +77 -9
- package/dist/task/TextSummaryTask.d.ts.map +1 -1
- package/dist/task/TextTranslationTask.d.ts +77 -9
- package/dist/task/TextTranslationTask.d.ts.map +1 -1
- package/dist/task/base/AiTask.d.ts +15 -6
- package/dist/task/base/AiTask.d.ts.map +1 -1
- package/dist/task/base/AiTaskSchemas.d.ts +43 -2
- package/dist/task/base/AiTaskSchemas.d.ts.map +1 -1
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -12
- package/dist/model/Model.d.ts +0 -26
- package/dist/model/Model.d.ts.map +0 -1
- package/dist/model/storage/InMemoryModelRepository.d.ts.map +0 -1
- package/dist/model/storage/IndexedDbModelRepository.d.ts +0 -18
- package/dist/model/storage/IndexedDbModelRepository.d.ts.map +0 -1
- package/dist/model/storage/PostgresModelRepository.d.ts +0 -19
- package/dist/model/storage/PostgresModelRepository.d.ts.map +0 -1
- package/dist/model/storage/SqliteModelRepository.d.ts +0 -18
- package/dist/model/storage/SqliteModelRepository.d.ts.map +0 -1
package/dist/bun.js
CHANGED
|
@@ -7,31 +7,14 @@ import {
|
|
|
7
7
|
PermanentJobError
|
|
8
8
|
} from "@workglow/job-queue";
|
|
9
9
|
|
|
10
|
-
// src/model/
|
|
10
|
+
// src/model/ModelRegistry.ts
|
|
11
|
+
import { createServiceToken, globalServiceRegistry } from "@workglow/util";
|
|
12
|
+
|
|
13
|
+
// src/model/InMemoryModelRepository.ts
|
|
11
14
|
import { InMemoryTabularRepository } from "@workglow/storage";
|
|
12
15
|
|
|
13
16
|
// src/model/ModelRepository.ts
|
|
14
17
|
import { EventEmitter } from "@workglow/util";
|
|
15
|
-
var ModelSchema = {
|
|
16
|
-
type: "object",
|
|
17
|
-
properties: {
|
|
18
|
-
name: { type: "string" },
|
|
19
|
-
details: { type: "string" }
|
|
20
|
-
},
|
|
21
|
-
required: ["name", "details"],
|
|
22
|
-
additionalProperties: false
|
|
23
|
-
};
|
|
24
|
-
var ModelPrimaryKeyNames = ["name"];
|
|
25
|
-
var Task2ModelSchema = {
|
|
26
|
-
type: "object",
|
|
27
|
-
properties: {
|
|
28
|
-
task: { type: "string" },
|
|
29
|
-
model: { type: "string" }
|
|
30
|
-
},
|
|
31
|
-
required: ["task", "model"],
|
|
32
|
-
additionalProperties: false
|
|
33
|
-
};
|
|
34
|
-
var Task2ModelPrimaryKeyNames = ["task", "model"];
|
|
35
18
|
|
|
36
19
|
class ModelRepository {
|
|
37
20
|
type = "ModelRepository";
|
|
@@ -49,101 +32,101 @@ class ModelRepository {
|
|
|
49
32
|
return this.events.waitOn(name);
|
|
50
33
|
}
|
|
51
34
|
async addModel(model) {
|
|
52
|
-
await this.modelTabularRepository.put(
|
|
53
|
-
this.models.set(model.name, model);
|
|
35
|
+
await this.modelTabularRepository.put(model);
|
|
54
36
|
this.events.emit("model_added", model);
|
|
55
37
|
return model;
|
|
56
38
|
}
|
|
57
39
|
async findModelsByTask(task) {
|
|
58
40
|
if (typeof task != "string")
|
|
59
41
|
return;
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
42
|
+
const allModels = await this.modelTabularRepository.getAll();
|
|
43
|
+
if (!allModels || allModels.length === 0)
|
|
44
|
+
return;
|
|
45
|
+
const models = allModels.filter((model) => model.tasks?.includes(task));
|
|
46
|
+
if (models.length === 0)
|
|
62
47
|
return;
|
|
63
|
-
const models = [];
|
|
64
|
-
for (const junction of junctions) {
|
|
65
|
-
const model = await this.modelTabularRepository.get({ name: junction.model });
|
|
66
|
-
if (model)
|
|
67
|
-
models.push(JSON.parse(model.details));
|
|
68
|
-
}
|
|
69
|
-
models.forEach((m) => this.models.set(m.name, m));
|
|
70
|
-
this.taskModels.set(task, models);
|
|
71
48
|
return models;
|
|
72
49
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
async findTasksByModel(model) {
|
|
76
|
-
if (typeof model != "string")
|
|
50
|
+
async findTasksByModel(model_id) {
|
|
51
|
+
if (typeof model_id != "string")
|
|
77
52
|
return;
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
53
|
+
const modelRecord = await this.modelTabularRepository.get({ model_id });
|
|
54
|
+
if (!modelRecord)
|
|
80
55
|
return;
|
|
81
|
-
return
|
|
56
|
+
return modelRecord.tasks && modelRecord.tasks.length > 0 ? modelRecord.tasks : undefined;
|
|
82
57
|
}
|
|
83
58
|
async enumerateAllTasks() {
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
59
|
+
const allModels = await this.modelTabularRepository.getAll();
|
|
60
|
+
if (!allModels || allModels.length === 0)
|
|
86
61
|
return;
|
|
87
|
-
const uniqueTasks =
|
|
88
|
-
|
|
62
|
+
const uniqueTasks = new Set;
|
|
63
|
+
for (const model of allModels) {
|
|
64
|
+
if (model.tasks) {
|
|
65
|
+
for (const task of model.tasks) {
|
|
66
|
+
uniqueTasks.add(task);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return uniqueTasks.size > 0 ? Array.from(uniqueTasks) : undefined;
|
|
89
71
|
}
|
|
90
72
|
async enumerateAllModels() {
|
|
91
73
|
const models = await this.modelTabularRepository.getAll();
|
|
92
74
|
if (!models || models.length === 0)
|
|
93
75
|
return;
|
|
94
|
-
|
|
95
|
-
parsedModels.forEach((m) => this.models.set(m.name, m));
|
|
96
|
-
return parsedModels;
|
|
97
|
-
}
|
|
98
|
-
async connectTaskToModel(task, model) {
|
|
99
|
-
await this.task2ModelTabularRepository.put({ task, model });
|
|
100
|
-
this.events.emit("task_model_connected", task, model);
|
|
76
|
+
return models;
|
|
101
77
|
}
|
|
102
|
-
async findByName(
|
|
103
|
-
if (typeof
|
|
78
|
+
async findByName(model_id) {
|
|
79
|
+
if (typeof model_id != "string")
|
|
104
80
|
return;
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
return;
|
|
108
|
-
const model = JSON.parse(modelstr.details);
|
|
109
|
-
this.models.set(model.name, model);
|
|
110
|
-
return model;
|
|
81
|
+
const model = await this.modelTabularRepository.get({ model_id });
|
|
82
|
+
return model ?? undefined;
|
|
111
83
|
}
|
|
112
84
|
async size() {
|
|
113
85
|
return await this.modelTabularRepository.size();
|
|
114
86
|
}
|
|
115
|
-
async clear() {
|
|
116
|
-
await this.modelTabularRepository.deleteAll();
|
|
117
|
-
}
|
|
118
87
|
}
|
|
119
88
|
|
|
120
|
-
// src/model/
|
|
89
|
+
// src/model/ModelSchema.ts
|
|
90
|
+
var ModelSchema = {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
model_id: { type: "string" },
|
|
94
|
+
tasks: { type: "array", items: { type: "string" } },
|
|
95
|
+
title: { type: "string" },
|
|
96
|
+
description: { type: "string" },
|
|
97
|
+
provider: { type: "string" },
|
|
98
|
+
providerConfig: { type: "object", default: {} },
|
|
99
|
+
metadata: { type: "object", default: {} }
|
|
100
|
+
},
|
|
101
|
+
required: ["model_id", "tasks", "provider", "title", "description", "providerConfig", "metadata"],
|
|
102
|
+
additionalProperties: false
|
|
103
|
+
};
|
|
104
|
+
var ModelPrimaryKeyNames = ["model_id"];
|
|
105
|
+
|
|
106
|
+
// src/model/InMemoryModelRepository.ts
|
|
121
107
|
class InMemoryModelRepository extends ModelRepository {
|
|
122
108
|
modelTabularRepository;
|
|
123
|
-
task2ModelTabularRepository;
|
|
124
109
|
type = "InMemoryModelRepository";
|
|
125
110
|
constructor() {
|
|
126
111
|
super();
|
|
127
112
|
this.modelTabularRepository = new InMemoryTabularRepository(ModelSchema, ModelPrimaryKeyNames);
|
|
128
|
-
this.task2ModelTabularRepository = new InMemoryTabularRepository(Task2ModelSchema, Task2ModelPrimaryKeyNames, ["model"]);
|
|
129
113
|
}
|
|
130
114
|
}
|
|
131
115
|
|
|
132
116
|
// src/model/ModelRegistry.ts
|
|
133
|
-
|
|
117
|
+
var MODEL_REPOSITORY = createServiceToken("model.repository");
|
|
118
|
+
if (!globalServiceRegistry.has(MODEL_REPOSITORY)) {
|
|
119
|
+
globalServiceRegistry.register(MODEL_REPOSITORY, () => new InMemoryModelRepository, true);
|
|
134
120
|
}
|
|
135
|
-
var modelRegistry;
|
|
136
121
|
function getGlobalModelRepository() {
|
|
137
|
-
|
|
138
|
-
modelRegistry = new FallbackModelRegistry;
|
|
139
|
-
return modelRegistry;
|
|
122
|
+
return globalServiceRegistry.get(MODEL_REPOSITORY);
|
|
140
123
|
}
|
|
141
124
|
function setGlobalModelRepository(pr) {
|
|
142
|
-
|
|
125
|
+
globalServiceRegistry.registerInstance(MODEL_REPOSITORY, pr);
|
|
143
126
|
}
|
|
144
127
|
|
|
145
128
|
// src/provider/AiProviderRegistry.ts
|
|
146
|
-
import { globalServiceRegistry, WORKER_MANAGER } from "@workglow/util";
|
|
129
|
+
import { globalServiceRegistry as globalServiceRegistry2, WORKER_MANAGER } from "@workglow/util";
|
|
147
130
|
|
|
148
131
|
class AiProviderRegistry {
|
|
149
132
|
runFnRegistry = new Map;
|
|
@@ -155,7 +138,7 @@ class AiProviderRegistry {
|
|
|
155
138
|
}
|
|
156
139
|
registerAsWorkerRunFn(modelProvider, taskType) {
|
|
157
140
|
const workerFn = async (input, model, update_progress, signal) => {
|
|
158
|
-
const workerManager =
|
|
141
|
+
const workerManager = globalServiceRegistry2.get(WORKER_MANAGER);
|
|
159
142
|
const result = await workerManager.callWorkerFunction(modelProvider, taskType, [input, model], {
|
|
160
143
|
signal,
|
|
161
144
|
onProgress: update_progress
|
|
@@ -476,25 +459,29 @@ class AiTask extends JobQueueTask {
|
|
|
476
459
|
config.name ||= `${new.target.type || new.target.name}${input.model ? " with model " + input.model : ""}`;
|
|
477
460
|
super(input, config);
|
|
478
461
|
}
|
|
479
|
-
async
|
|
462
|
+
async getJobInput(input) {
|
|
480
463
|
if (typeof input.model !== "string") {
|
|
481
464
|
console.error("AiTask: Model is not a string", input);
|
|
482
465
|
throw new TaskConfigurationError("AiTask: Model is not a string, only create job for single model tasks");
|
|
483
466
|
}
|
|
484
467
|
const runtype = this.constructor.runtype ?? this.constructor.type;
|
|
485
468
|
const model = await this.getModelForInput(input);
|
|
486
|
-
|
|
487
|
-
|
|
469
|
+
return {
|
|
470
|
+
taskType: runtype,
|
|
471
|
+
aiProvider: model.provider,
|
|
472
|
+
taskInput: input
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async createJob(input, queueName) {
|
|
476
|
+
const jobInput = await this.getJobInput(input);
|
|
477
|
+
const resolvedQueueName = queueName ?? await this.getDefaultQueueName(input);
|
|
478
|
+
if (!resolvedQueueName) {
|
|
488
479
|
throw new TaskConfigurationError("JobQueueTask: Unable to determine queue for AI provider");
|
|
489
480
|
}
|
|
490
481
|
const job = new AiJob({
|
|
491
|
-
queueName,
|
|
482
|
+
queueName: resolvedQueueName,
|
|
492
483
|
jobRunId: this.config.runnerId,
|
|
493
|
-
input:
|
|
494
|
-
taskType: runtype,
|
|
495
|
-
aiProvider: model.provider,
|
|
496
|
-
taskInput: input
|
|
497
|
-
}
|
|
484
|
+
input: jobInput
|
|
498
485
|
});
|
|
499
486
|
return job;
|
|
500
487
|
}
|
|
@@ -533,7 +520,7 @@ class AiTask extends JobQueueTask {
|
|
|
533
520
|
for (const [key, propSchema] of modelTaskProperties) {
|
|
534
521
|
let requestedModels = Array.isArray(input[key]) ? input[key] : [input[key]];
|
|
535
522
|
for (const model of requestedModels) {
|
|
536
|
-
const foundModel = taskModels?.find((m) => m.
|
|
523
|
+
const foundModel = taskModels?.find((m) => m.model_id === model);
|
|
537
524
|
if (!foundModel) {
|
|
538
525
|
throw new TaskConfigurationError(`AiTask: Missing model for '${key}' named '${model}' for task '${this.type}'`);
|
|
539
526
|
}
|
|
@@ -567,7 +554,7 @@ class AiTask extends JobQueueTask {
|
|
|
567
554
|
const taskModels = await getGlobalModelRepository().findModelsByTask(this.type);
|
|
568
555
|
for (const [key, propSchema] of modelTaskProperties) {
|
|
569
556
|
let requestedModels = Array.isArray(input[key]) ? input[key] : [input[key]];
|
|
570
|
-
let usingModels = requestedModels.filter((model) => taskModels?.find((m) => m.
|
|
557
|
+
let usingModels = requestedModels.filter((model) => taskModels?.find((m) => m.model_id === model));
|
|
571
558
|
usingModels = usingModels.length > 1 ? usingModels : usingModels[0];
|
|
572
559
|
input[key] = usingModels;
|
|
573
560
|
}
|
|
@@ -664,7 +651,7 @@ var TypeLanguage = (annotations = {}) => ({
|
|
|
664
651
|
minLength: 2,
|
|
665
652
|
...annotations
|
|
666
653
|
});
|
|
667
|
-
function
|
|
654
|
+
function TypeModelAsString(semantic = "model", options = {}) {
|
|
668
655
|
if (semantic !== "model" && !semantic.startsWith("model:")) {
|
|
669
656
|
throw new Error("Invalid semantic value");
|
|
670
657
|
}
|
|
@@ -677,6 +664,11 @@ function TypeModel(semantic = "model", options = {}) {
|
|
|
677
664
|
type: "string"
|
|
678
665
|
};
|
|
679
666
|
}
|
|
667
|
+
function TypeModel(semantic = "model", options = {}) {
|
|
668
|
+
return {
|
|
669
|
+
oneOf: [TypeModelAsString(semantic, options), ModelSchema]
|
|
670
|
+
};
|
|
671
|
+
}
|
|
680
672
|
var TypeReplicateArray = (type, annotations = {}) => ({
|
|
681
673
|
oneOf: [type, { type: "array", items: type }],
|
|
682
674
|
title: type.title,
|
|
@@ -1118,11 +1110,15 @@ var TextTranslationInputSchema = {
|
|
|
1118
1110
|
}),
|
|
1119
1111
|
source_lang: TypeReplicateArray(TypeLanguage({
|
|
1120
1112
|
title: "Source Language",
|
|
1121
|
-
description: "The source language"
|
|
1113
|
+
description: "The source language",
|
|
1114
|
+
minLength: 2,
|
|
1115
|
+
maxLength: 2
|
|
1122
1116
|
})),
|
|
1123
1117
|
target_lang: TypeReplicateArray(TypeLanguage({
|
|
1124
1118
|
title: "Target Language",
|
|
1125
|
-
description: "The target language"
|
|
1119
|
+
description: "The target language",
|
|
1120
|
+
minLength: 2,
|
|
1121
|
+
maxLength: 2
|
|
1126
1122
|
})),
|
|
1127
1123
|
model: modelSchema7
|
|
1128
1124
|
},
|
|
@@ -1139,7 +1135,9 @@ var TextTranslationOutputSchema = {
|
|
|
1139
1135
|
},
|
|
1140
1136
|
target_lang: TypeLanguage({
|
|
1141
1137
|
title: "Output Language",
|
|
1142
|
-
description: "The output language"
|
|
1138
|
+
description: "The output language",
|
|
1139
|
+
minLength: 2,
|
|
1140
|
+
maxLength: 2
|
|
1143
1141
|
})
|
|
1144
1142
|
},
|
|
1145
1143
|
required: ["text", "target_lang"],
|
|
@@ -1294,42 +1292,6 @@ function normalize(vector) {
|
|
|
1294
1292
|
}
|
|
1295
1293
|
return new Float32Array(normalized);
|
|
1296
1294
|
}
|
|
1297
|
-
// src/model/storage/IndexedDbModelRepository.ts
|
|
1298
|
-
import { IndexedDbTabularRepository } from "@workglow/storage";
|
|
1299
|
-
class IndexedDbModelRepository extends ModelRepository {
|
|
1300
|
-
modelTabularRepository;
|
|
1301
|
-
task2ModelTabularRepository;
|
|
1302
|
-
type = "IndexedDbModelRepository";
|
|
1303
|
-
constructor(tableModels = "models", tableTask2Models = "task2models") {
|
|
1304
|
-
super();
|
|
1305
|
-
this.modelTabularRepository = new IndexedDbTabularRepository(tableModels, ModelSchema, ModelPrimaryKeyNames);
|
|
1306
|
-
this.task2ModelTabularRepository = new IndexedDbTabularRepository(tableTask2Models, Task2ModelSchema, Task2ModelPrimaryKeyNames, ["model"]);
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
// src/model/storage/PostgresModelRepository.ts
|
|
1310
|
-
import { PostgresTabularRepository } from "@workglow/storage";
|
|
1311
|
-
class PostgresModelRepository extends ModelRepository {
|
|
1312
|
-
type = "PostgresModelRepository";
|
|
1313
|
-
modelTabularRepository;
|
|
1314
|
-
task2ModelTabularRepository;
|
|
1315
|
-
constructor(db, tableModels = "aimodel", tableTask2Models = "aitask2aimodel") {
|
|
1316
|
-
super();
|
|
1317
|
-
this.modelTabularRepository = new PostgresTabularRepository(db, tableModels, ModelSchema, ModelPrimaryKeyNames);
|
|
1318
|
-
this.task2ModelTabularRepository = new PostgresTabularRepository(db, tableTask2Models, Task2ModelSchema, Task2ModelPrimaryKeyNames, ["model"]);
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
// src/model/storage/SqliteModelRepository.ts
|
|
1322
|
-
import { SqliteTabularRepository } from "@workglow/storage";
|
|
1323
|
-
class SqliteModelRepository extends ModelRepository {
|
|
1324
|
-
type = "SqliteModelRepository";
|
|
1325
|
-
modelTabularRepository;
|
|
1326
|
-
task2ModelTabularRepository;
|
|
1327
|
-
constructor(dbOrPath, tableModels = "aimodel", tableTask2Models = "aitask2aimodel") {
|
|
1328
|
-
super();
|
|
1329
|
-
this.modelTabularRepository = new SqliteTabularRepository(dbOrPath, tableModels, ModelSchema, ModelPrimaryKeyNames);
|
|
1330
|
-
this.task2ModelTabularRepository = new SqliteTabularRepository(dbOrPath, tableTask2Models, Task2ModelSchema, Task2ModelPrimaryKeyNames, ["model"]);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
1295
|
export {
|
|
1334
1296
|
setGlobalModelRepository,
|
|
1335
1297
|
setAiProviderRegistry,
|
|
@@ -1341,6 +1303,7 @@ export {
|
|
|
1341
1303
|
VectorSimilarityTask,
|
|
1342
1304
|
TypedArraySchema,
|
|
1343
1305
|
TypeReplicateArray,
|
|
1306
|
+
TypeModelAsString,
|
|
1344
1307
|
TypeModel,
|
|
1345
1308
|
TypeLanguage,
|
|
1346
1309
|
TextTranslationTask,
|
|
@@ -1368,17 +1331,13 @@ export {
|
|
|
1368
1331
|
TextEmbeddingOutputSchema,
|
|
1369
1332
|
TextEmbeddingInputSchema,
|
|
1370
1333
|
TextEmbedding,
|
|
1371
|
-
Task2ModelSchema,
|
|
1372
|
-
Task2ModelPrimaryKeyNames,
|
|
1373
1334
|
TableFragment,
|
|
1374
|
-
SqliteModelRepository,
|
|
1375
1335
|
SimilarityFn,
|
|
1376
1336
|
Similarity,
|
|
1377
|
-
PostgresModelRepository,
|
|
1378
1337
|
ModelSchema,
|
|
1379
1338
|
ModelRepository,
|
|
1380
1339
|
ModelPrimaryKeyNames,
|
|
1381
|
-
|
|
1340
|
+
MODEL_REPOSITORY,
|
|
1382
1341
|
InMemoryModelRepository,
|
|
1383
1342
|
ImageFragment,
|
|
1384
1343
|
DownloadModelTask,
|
|
@@ -1395,4 +1354,4 @@ export {
|
|
|
1395
1354
|
AiJob
|
|
1396
1355
|
};
|
|
1397
1356
|
|
|
1398
|
-
//# debugId=
|
|
1357
|
+
//# debugId=B7F9C684EE8646AC64756E2164756E21
|