@lokascript/framework 4.5.0 → 4.6.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/CHANGELOG.md +134 -8
- package/dist/api/create-dsl.d.ts +46 -1
- package/dist/api/create-dsl.d.ts.map +1 -1
- package/dist/api/domain-registry.d.ts.map +1 -1
- package/dist/api/index.js +187 -19
- package/dist/api/index.js.map +1 -1
- package/dist/api/reading.d.ts +8 -0
- package/dist/api/reading.d.ts.map +1 -0
- package/dist/index.cjs +189 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +187 -19
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__test__/fixtures/sql-dsl.ts +3 -0
- package/src/__test__/sql-integration.test.ts +2 -2
- package/src/api/create-dsl.test.ts +120 -0
- package/src/api/create-dsl.ts +173 -21
- package/src/api/domain-registry.test.ts +70 -5
- package/src/api/domain-registry.ts +82 -1
- package/src/api/reading.ts +29 -0
package/dist/index.js
CHANGED
|
@@ -2818,7 +2818,53 @@ function semanticValueToAST(value) {
|
|
|
2818
2818
|
}
|
|
2819
2819
|
}
|
|
2820
2820
|
|
|
2821
|
+
// src/api/reading.ts
|
|
2822
|
+
function roleValues(node) {
|
|
2823
|
+
const out = /* @__PURE__ */ new Map();
|
|
2824
|
+
for (const [role, value] of node.roles) {
|
|
2825
|
+
const v = value;
|
|
2826
|
+
out.set(role, String(v?.value ?? v?.raw ?? value));
|
|
2827
|
+
}
|
|
2828
|
+
return out;
|
|
2829
|
+
}
|
|
2830
|
+
function readingDifference(source, back) {
|
|
2831
|
+
if (back.action !== source.action) return `reads as ${back.action}`;
|
|
2832
|
+
const want = roleValues(source);
|
|
2833
|
+
const got = roleValues(back);
|
|
2834
|
+
const parts = [];
|
|
2835
|
+
for (const [role, value] of want) {
|
|
2836
|
+
if (!got.has(role)) parts.push(`${role} lost`);
|
|
2837
|
+
else if (got.get(role) !== value) parts.push(`${role}: ${value} \u2192 ${got.get(role)}`);
|
|
2838
|
+
}
|
|
2839
|
+
for (const role of got.keys()) if (!want.has(role)) parts.push(`${role} added`);
|
|
2840
|
+
return parts.join(", ");
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2821
2843
|
// src/api/create-dsl.ts
|
|
2844
|
+
var UnreadInputError = class extends Error {
|
|
2845
|
+
constructor(input, language, reading, unread) {
|
|
2846
|
+
super(`"${input}" (${language}) reads as ${reading.action} only by leaving "${unread}" unread`);
|
|
2847
|
+
this.input = input;
|
|
2848
|
+
this.language = language;
|
|
2849
|
+
this.reading = reading;
|
|
2850
|
+
this.unread = unread;
|
|
2851
|
+
this.code = "unread-input";
|
|
2852
|
+
this.name = "UnreadInputError";
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
var TranslationReadBackError = class extends Error {
|
|
2856
|
+
constructor(partial, from, to, source, reading, why) {
|
|
2857
|
+
super(`translation ${from} \u2192 ${to} does not read back as the source (${why}): "${partial}"`);
|
|
2858
|
+
this.partial = partial;
|
|
2859
|
+
this.from = from;
|
|
2860
|
+
this.to = to;
|
|
2861
|
+
this.source = source;
|
|
2862
|
+
this.reading = reading;
|
|
2863
|
+
this.why = why;
|
|
2864
|
+
this.code = "translation-does-not-read-back";
|
|
2865
|
+
this.name = "TranslationReadBackError";
|
|
2866
|
+
}
|
|
2867
|
+
};
|
|
2822
2868
|
var DSLRegistry = class {
|
|
2823
2869
|
/**
|
|
2824
2870
|
* @param partiallyTranslatedActions Actions permitted to lack a keyword in
|
|
@@ -2903,25 +2949,50 @@ var MultilingualDSLImpl = class {
|
|
|
2903
2949
|
const profile = {
|
|
2904
2950
|
code: language
|
|
2905
2951
|
};
|
|
2952
|
+
const total = tokens.tokens.length;
|
|
2953
|
+
let best = null;
|
|
2954
|
+
let longestPartial = null;
|
|
2906
2955
|
for (const pattern of patterns) {
|
|
2956
|
+
const mark = tokens.mark();
|
|
2907
2957
|
const match = this.matcher.matchPattern(tokens, pattern, profile);
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
sourceLanguage: language,
|
|
2915
|
-
sourceText: input,
|
|
2916
|
-
patternId: pattern.id,
|
|
2917
|
-
confidence: match.confidence
|
|
2918
|
-
}
|
|
2919
|
-
};
|
|
2920
|
-
return { node, confidence: match.confidence };
|
|
2958
|
+
tokens.reset(mark);
|
|
2959
|
+
if (!match) continue;
|
|
2960
|
+
if (match.consumedTokens < total) {
|
|
2961
|
+
if (!longestPartial || match.consumedTokens > longestPartial.consumedTokens)
|
|
2962
|
+
longestPartial = match;
|
|
2963
|
+
continue;
|
|
2921
2964
|
}
|
|
2965
|
+
if (!best || match.pattern.priority > best.pattern.priority || match.pattern.priority === best.pattern.priority && match.confidence > best.confidence + 1e-3)
|
|
2966
|
+
best = match;
|
|
2967
|
+
}
|
|
2968
|
+
if (best) {
|
|
2969
|
+
const node = this.nodeOf(best, input, language);
|
|
2970
|
+
return { node, confidence: best.confidence };
|
|
2971
|
+
}
|
|
2972
|
+
if (longestPartial) {
|
|
2973
|
+
const unread = tokens.tokens.slice(longestPartial.consumedTokens).map((t) => t.value).join(" ");
|
|
2974
|
+
throw new UnreadInputError(
|
|
2975
|
+
input,
|
|
2976
|
+
language,
|
|
2977
|
+
this.nodeOf(longestPartial, input, language),
|
|
2978
|
+
unread
|
|
2979
|
+
);
|
|
2922
2980
|
}
|
|
2923
2981
|
throw new Error(`No pattern matched for input: ${input}`);
|
|
2924
2982
|
}
|
|
2983
|
+
nodeOf(match, input, language) {
|
|
2984
|
+
return {
|
|
2985
|
+
kind: "command",
|
|
2986
|
+
action: match.pattern.command,
|
|
2987
|
+
roles: match.captured,
|
|
2988
|
+
metadata: {
|
|
2989
|
+
sourceLanguage: language,
|
|
2990
|
+
sourceText: input,
|
|
2991
|
+
patternId: match.pattern.id,
|
|
2992
|
+
confidence: match.confidence
|
|
2993
|
+
}
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2925
2996
|
validate(input, language) {
|
|
2926
2997
|
try {
|
|
2927
2998
|
const result = this.parseWithConfidence(input, language);
|
|
@@ -2932,11 +3003,12 @@ var MultilingualDSLImpl = class {
|
|
|
2932
3003
|
} catch (error) {
|
|
2933
3004
|
return {
|
|
2934
3005
|
valid: false,
|
|
2935
|
-
errors: [error instanceof Error ? error.message : String(error)]
|
|
3006
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
3007
|
+
...error instanceof UnreadInputError ? { errorCode: error.code } : {}
|
|
2936
3008
|
};
|
|
2937
3009
|
}
|
|
2938
3010
|
}
|
|
2939
|
-
translate(input, fromLanguage, toLanguage) {
|
|
3011
|
+
translate(input, fromLanguage, toLanguage, options) {
|
|
2940
3012
|
if (isExplicitSyntax(input)) return input;
|
|
2941
3013
|
let node;
|
|
2942
3014
|
try {
|
|
@@ -2948,14 +3020,46 @@ var MultilingualDSLImpl = class {
|
|
|
2948
3020
|
throw parseError;
|
|
2949
3021
|
}
|
|
2950
3022
|
const rendered = this.registry.getSupportedLanguages().includes(toLanguage) ? this.render(node, toLanguage) : null;
|
|
2951
|
-
if (rendered != null) return rendered;
|
|
3023
|
+
if (rendered != null) return this.readsBack(rendered, node, fromLanguage, toLanguage, options);
|
|
2952
3024
|
if (this.hasGrammarProfile(fromLanguage) && this.hasGrammarProfile(toLanguage)) {
|
|
2953
|
-
return this.
|
|
3025
|
+
return this.readsBack(
|
|
3026
|
+
this.transformer.transform(input, fromLanguage, toLanguage),
|
|
3027
|
+
node,
|
|
3028
|
+
fromLanguage,
|
|
3029
|
+
toLanguage,
|
|
3030
|
+
options
|
|
3031
|
+
);
|
|
2954
3032
|
}
|
|
2955
3033
|
throw new Error(
|
|
2956
3034
|
`translate() could not render action "${node.action}" in language "${toLanguage}": no custom renderer, domain renderer, or schema pattern covers it, and no grammar profile is configured for the transformer fallback. Either add "${toLanguage}" to the DSL's languages/renderer, or set 'grammarProfile' on its LanguageConfig in createMultilingualDSL() (parse/validate/compile do not need it).`
|
|
2957
3035
|
);
|
|
2958
3036
|
}
|
|
3037
|
+
/**
|
|
3038
|
+
* `rendered`, read in `to`, must be `source`: the same action and role values, or, when the DSL
|
|
3039
|
+
* compiles, the same code (a parser may keep a translated word its generator normalizes: bdd ja
|
|
3040
|
+
* `表示` is `visible`). The rule is the domain gate's (lokascript-domains). A translation used to be
|
|
3041
|
+
* returned whatever it read as: ja/ko bdd `select` dropped its value, and nothing said so.
|
|
3042
|
+
*/
|
|
3043
|
+
readsBack(rendered, source, from, to, options) {
|
|
3044
|
+
if (options?.lossy === "allow") return rendered;
|
|
3045
|
+
let back;
|
|
3046
|
+
try {
|
|
3047
|
+
back = this.parseWithConfidence(rendered, to).node;
|
|
3048
|
+
} catch (error) {
|
|
3049
|
+
const why = error instanceof Error ? error.message : String(error);
|
|
3050
|
+
throw new TranslationReadBackError(rendered, from, to, source, null, why);
|
|
3051
|
+
}
|
|
3052
|
+
const difference = readingDifference(source, back);
|
|
3053
|
+
if (!difference) return rendered;
|
|
3054
|
+
if (this.codeGenerator) {
|
|
3055
|
+
try {
|
|
3056
|
+
if (this.codeGenerator.generate(back) === this.codeGenerator.generate(source))
|
|
3057
|
+
return rendered;
|
|
3058
|
+
} catch {
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
throw new TranslationReadBackError(rendered, from, to, source, back, difference);
|
|
3062
|
+
}
|
|
2959
3063
|
hasGrammarProfile(language) {
|
|
2960
3064
|
return this.profileProvider.getProfile(language) != null;
|
|
2961
3065
|
}
|
|
@@ -2981,7 +3085,8 @@ var MultilingualDSLImpl = class {
|
|
|
2981
3085
|
} catch (error) {
|
|
2982
3086
|
return {
|
|
2983
3087
|
ok: false,
|
|
2984
|
-
errors: [error instanceof Error ? error.message : String(error)]
|
|
3088
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
3089
|
+
...error instanceof UnreadInputError ? { errorCode: error.code } : {}
|
|
2985
3090
|
};
|
|
2986
3091
|
}
|
|
2987
3092
|
}
|
|
@@ -3908,6 +4013,8 @@ var DomainRegistry = class {
|
|
|
3908
4013
|
return jsonResponse({ error: `Unknown operation: ${parsed.operation}` }, true);
|
|
3909
4014
|
}
|
|
3910
4015
|
} catch (error) {
|
|
4016
|
+
if (error instanceof UnreadInputError) return unreadInputResponse(error);
|
|
4017
|
+
if (error instanceof TranslationReadBackError) return readBackResponse(error);
|
|
3911
4018
|
const message = error instanceof Error ? error.message : String(error);
|
|
3912
4019
|
return jsonResponse({ error: `${descriptor.name} tool error: ${message}` }, true);
|
|
3913
4020
|
}
|
|
@@ -4033,6 +4140,7 @@ var DomainRegistry = class {
|
|
|
4033
4140
|
ok: result.ok,
|
|
4034
4141
|
code: result.code,
|
|
4035
4142
|
errors: result.errors,
|
|
4143
|
+
...result.errorCode === "unread-input" ? { diagnostics: [UNREAD_INPUT(result.errors)] } : {},
|
|
4036
4144
|
language,
|
|
4037
4145
|
input
|
|
4038
4146
|
});
|
|
@@ -4045,6 +4153,7 @@ var DomainRegistry = class {
|
|
|
4045
4153
|
return jsonResponse({
|
|
4046
4154
|
valid: result.valid,
|
|
4047
4155
|
errors: result.errors,
|
|
4156
|
+
...result.errorCode === "unread-input" ? { diagnostics: [UNREAD_INPUT(result.errors)] } : {},
|
|
4048
4157
|
language,
|
|
4049
4158
|
[descriptor.inputLabel]: input
|
|
4050
4159
|
});
|
|
@@ -4075,7 +4184,24 @@ var DomainRegistry = class {
|
|
|
4075
4184
|
for (const [key, value] of node.roles) {
|
|
4076
4185
|
roles[key] = value;
|
|
4077
4186
|
}
|
|
4187
|
+
if (rendered != null) {
|
|
4188
|
+
let back = null;
|
|
4189
|
+
let why = "";
|
|
4190
|
+
try {
|
|
4191
|
+
back = dsl.parse(rendered, to);
|
|
4192
|
+
why = readingDifference(node, back);
|
|
4193
|
+
if (why && compiled.ok) {
|
|
4194
|
+
const again = dsl.compile(rendered, to);
|
|
4195
|
+
if (again.ok && again.code === compiled.code) why = "";
|
|
4196
|
+
}
|
|
4197
|
+
} catch (error) {
|
|
4198
|
+
why = error instanceof Error ? error.message : String(error);
|
|
4199
|
+
}
|
|
4200
|
+
if (why)
|
|
4201
|
+
return readBackResponse(new TranslationReadBackError(rendered, from, to, node, back, why));
|
|
4202
|
+
}
|
|
4078
4203
|
return jsonResponse({
|
|
4204
|
+
ok: true,
|
|
4079
4205
|
input: { [descriptor.inputLabel]: input, language: from },
|
|
4080
4206
|
...rendered != null && { rendered: { text: rendered, language: to } },
|
|
4081
4207
|
semantic: { action: node.action, roles },
|
|
@@ -4155,7 +4281,7 @@ function generateToolDefinitions(desc) {
|
|
|
4155
4281
|
case "translate":
|
|
4156
4282
|
tools.push({
|
|
4157
4283
|
name: `translate_${desc.name}`,
|
|
4158
|
-
description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
|
|
4284
|
+
description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}. A rendering that does not read back as the source is REFUSED: ok is false, the diagnostic is TRANSLATION_DOES_NOT_READ_BACK, and its text is under \`partial\`, never \`rendered\`: do not present it as the translation. Input left unread is refused as UNREAD_INPUT.`,
|
|
4159
4285
|
inputSchema: {
|
|
4160
4286
|
type: "object",
|
|
4161
4287
|
properties: {
|
|
@@ -4189,6 +4315,46 @@ function getString(args, name, defaultValue = "") {
|
|
|
4189
4315
|
const value = args[name];
|
|
4190
4316
|
return typeof value === "string" ? value : defaultValue;
|
|
4191
4317
|
}
|
|
4318
|
+
function readingOf(node) {
|
|
4319
|
+
const roles = {};
|
|
4320
|
+
for (const [key, value] of node.roles) roles[key] = value;
|
|
4321
|
+
return { action: node.action, roles };
|
|
4322
|
+
}
|
|
4323
|
+
var UNREAD_INPUT = (errors) => ({
|
|
4324
|
+
severity: "error",
|
|
4325
|
+
code: "UNREAD_INPUT",
|
|
4326
|
+
message: errors?.[0] ?? "part of the input was not read",
|
|
4327
|
+
suggestion: "Part of the input belongs to no role of the command it reads as: remove it, or quote a value of several words."
|
|
4328
|
+
});
|
|
4329
|
+
function unreadInputResponse(error) {
|
|
4330
|
+
return jsonResponse(
|
|
4331
|
+
{
|
|
4332
|
+
ok: false,
|
|
4333
|
+
unread: error.unread,
|
|
4334
|
+
reading: readingOf(error.reading),
|
|
4335
|
+
diagnostics: [UNREAD_INPUT([error.message])]
|
|
4336
|
+
},
|
|
4337
|
+
true
|
|
4338
|
+
);
|
|
4339
|
+
}
|
|
4340
|
+
function readBackResponse(error) {
|
|
4341
|
+
return jsonResponse(
|
|
4342
|
+
{
|
|
4343
|
+
ok: false,
|
|
4344
|
+
partial: error.partial,
|
|
4345
|
+
...error.reading ? { reading: readingOf(error.reading) } : {},
|
|
4346
|
+
diagnostics: [
|
|
4347
|
+
{
|
|
4348
|
+
severity: "error",
|
|
4349
|
+
code: "TRANSLATION_DOES_NOT_READ_BACK",
|
|
4350
|
+
message: error.message,
|
|
4351
|
+
suggestion: "The rendering would not mean the source in the target language: do not present `partial` as the translation."
|
|
4352
|
+
}
|
|
4353
|
+
]
|
|
4354
|
+
},
|
|
4355
|
+
true
|
|
4356
|
+
);
|
|
4357
|
+
}
|
|
4192
4358
|
function jsonResponse(data, isError) {
|
|
4193
4359
|
return {
|
|
4194
4360
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
@@ -6599,7 +6765,9 @@ export {
|
|
|
6599
6765
|
PunctuationExtractor,
|
|
6600
6766
|
StringLiteralExtractor,
|
|
6601
6767
|
TokenStreamImpl,
|
|
6768
|
+
TranslationReadBackError,
|
|
6602
6769
|
UnicodeIdentifierExtractor,
|
|
6770
|
+
UnreadInputError,
|
|
6603
6771
|
WhitespaceExtractor,
|
|
6604
6772
|
accumulateBlocks,
|
|
6605
6773
|
buildDisambiguation,
|