typia 14.0.6 → 15.0.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 +3 -0
- package/lib/http.d.ts +49 -24
- package/lib/http.js.map +1 -1
- package/lib/http.mjs.map +1 -1
- package/lib/internal/_createLlmEvaluation.d.ts +1 -0
- package/lib/internal/_createLlmEvaluation.js +486 -0
- package/lib/internal/_createLlmEvaluation.js.map +1 -0
- package/lib/internal/_createLlmEvaluation.mjs +382 -0
- package/lib/internal/_createLlmEvaluation.mjs.map +1 -0
- package/lib/internal/_httpFormDataReadBigint.js +7 -5
- package/lib/internal/_httpFormDataReadBigint.js.map +1 -1
- package/lib/internal/_httpFormDataReadBigint.mjs +1 -1
- package/lib/internal/_httpFormDataReadBigint.mjs.map +1 -1
- package/lib/internal/_httpFormDataReadBoolean.js +12 -10
- package/lib/internal/_httpFormDataReadBoolean.js.map +1 -1
- package/lib/internal/_httpFormDataReadBoolean.mjs +1 -1
- package/lib/internal/_httpFormDataReadBoolean.mjs.map +1 -1
- package/lib/internal/_httpFormDataReadNumber.js +7 -5
- package/lib/internal/_httpFormDataReadNumber.js.map +1 -1
- package/lib/internal/_httpFormDataReadNumber.mjs +1 -1
- package/lib/internal/_httpFormDataReadNumber.mjs.map +1 -1
- package/lib/internal/_httpFormDataReadString.d.ts +9 -1
- package/lib/internal/_httpFormDataReadString.js +10 -2
- package/lib/internal/_httpFormDataReadString.js.map +1 -1
- package/lib/internal/_httpFormDataReadString.mjs +9 -1
- package/lib/internal/_httpFormDataReadString.mjs.map +1 -1
- package/lib/internal/_httpHeaderReadBigint.js +5 -1
- package/lib/internal/_httpHeaderReadBigint.js.map +1 -1
- package/lib/internal/_httpHeaderReadBigint.mjs +2 -1
- package/lib/internal/_httpHeaderReadBigint.mjs.map +1 -1
- package/lib/internal/_httpHeaderReadNumber.js +5 -1
- package/lib/internal/_httpHeaderReadNumber.js.map +1 -1
- package/lib/internal/_httpHeaderReadNumber.mjs +2 -1
- package/lib/internal/_httpHeaderReadNumber.mjs.map +1 -1
- package/lib/internal/_httpParameterReadBigint.js +4 -0
- package/lib/internal/_httpParameterReadBigint.js.map +1 -1
- package/lib/internal/_httpParameterReadBigint.mjs +1 -0
- package/lib/internal/_httpParameterReadBigint.mjs.map +1 -1
- package/lib/internal/_httpParameterReadNumber.js +4 -0
- package/lib/internal/_httpParameterReadNumber.js.map +1 -1
- package/lib/internal/_httpParameterReadNumber.mjs +1 -0
- package/lib/internal/_httpParameterReadNumber.mjs.map +1 -1
- package/lib/internal/_httpParameterReadString.d.ts +9 -1
- package/lib/internal/_httpParameterReadString.js +9 -1
- package/lib/internal/_httpParameterReadString.js.map +1 -1
- package/lib/internal/_httpParameterReadString.mjs +9 -1
- package/lib/internal/_httpParameterReadString.mjs.map +1 -1
- package/lib/internal/_httpQueryReadBigint.js +8 -1
- package/lib/internal/_httpQueryReadBigint.js.map +1 -1
- package/lib/internal/_httpQueryReadBigint.mjs +1 -1
- package/lib/internal/_httpQueryReadBigint.mjs.map +1 -1
- package/lib/internal/_httpQueryReadBoolean.js +4 -1
- package/lib/internal/_httpQueryReadBoolean.js.map +1 -1
- package/lib/internal/_httpQueryReadBoolean.mjs +1 -1
- package/lib/internal/_httpQueryReadBoolean.mjs.map +1 -1
- package/lib/internal/_httpQueryReadNumber.js +8 -1
- package/lib/internal/_httpQueryReadNumber.js.map +1 -1
- package/lib/internal/_httpQueryReadNumber.mjs +1 -1
- package/lib/internal/_httpQueryReadNumber.mjs.map +1 -1
- package/lib/internal/_httpQueryReadString.d.ts +9 -1
- package/lib/internal/_httpQueryReadString.js +9 -1
- package/lib/internal/_httpQueryReadString.js.map +1 -1
- package/lib/internal/_httpQueryReadString.mjs +9 -1
- package/lib/internal/_httpQueryReadString.mjs.map +1 -1
- package/lib/internal/_randomBigint.d.ts +12 -1
- package/lib/internal/_randomBigint.js +90 -2
- package/lib/internal/_randomBigint.js.map +1 -1
- package/lib/internal/_randomBigint.mjs +70 -2
- package/lib/internal/_randomBigint.mjs.map +1 -1
- package/lib/internal/_randomInteger.js +21 -12
- package/lib/internal/_randomInteger.js.map +1 -1
- package/lib/internal/_randomInteger.mjs +21 -16
- package/lib/internal/_randomInteger.mjs.map +1 -1
- package/lib/internal/_randomNumber.js +19 -10
- package/lib/internal/_randomNumber.js.map +1 -1
- package/lib/internal/_randomNumber.mjs +19 -14
- package/lib/internal/_randomNumber.mjs.map +1 -1
- package/lib/internal/_stringLengthGte.d.ts +1 -0
- package/lib/internal/_stringLengthGte.js +14 -0
- package/lib/internal/_stringLengthGte.js.map +1 -0
- package/lib/internal/_stringLengthGte.mjs +11 -0
- package/lib/internal/_stringLengthGte.mjs.map +1 -0
- package/lib/internal/_stringLengthLte.d.ts +1 -0
- package/lib/internal/_stringLengthLte.js +14 -0
- package/lib/internal/_stringLengthLte.js.map +1 -0
- package/lib/internal/_stringLengthLte.mjs +11 -0
- package/lib/internal/_stringLengthLte.mjs.map +1 -0
- package/lib/llm.d.ts +54 -1
- package/lib/llm.js +5 -0
- package/lib/llm.js.map +1 -1
- package/lib/llm.mjs +6 -1
- package/lib/llm.mjs.map +1 -1
- package/lib/re-exports.d.ts +1 -1
- package/lib/re-exports.js.map +1 -1
- package/native/cmd/ttsc-typia/build.go +2 -0
- package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +1 -1
- package/native/cmd/ttsc-typia/llm_evaluation_accepts_property_probability_test.go +27 -0
- package/native/cmd/ttsc-typia/llm_evaluation_accepts_unselected_source_probability_test.go +26 -0
- package/native/cmd/ttsc-typia/llm_evaluation_complete_object_shape_test.go +96 -0
- package/native/cmd/ttsc-typia/llm_evaluation_ignores_declaration_probability_without_call_test.go +18 -0
- package/native/cmd/ttsc-typia/llm_evaluation_plan_emission_transform_test.go +90 -0
- package/native/cmd/ttsc-typia/llm_evaluation_program_scan_dependencies_test.go +69 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_comment_test.go +44 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_refinement_test.go +45 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_computed_method_probability_test.go +30 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_imported_declaration_probability_test.go +48 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_invalid_probability_test.go +208 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_refinements_test.go +94 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_undecidable_types_test.go +232 -0
- package/native/cmd/ttsc-typia/metadata_type_tag_sole_malformed_diagnostic_test.go +94 -0
- package/native/cmd/ttsc-typia/random_recursive_min_items_diagnostic_test.go +18 -1
- package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -2
- package/native/cmd/ttsc-typia/transform.go +2 -0
- package/native/cmd/ttsc-typia/transform_helpers_test.go +17 -3
- package/native/core/context/ITypiaContext.go +4 -0
- package/native/core/factories/ExpressionFactory.go +16 -1
- package/native/core/factories/IdentifierFactory.go +10 -0
- package/native/core/factories/LiteralFactory.go +16 -0
- package/native/core/factories/MetadataCommentTagFactory.go +185 -35
- package/native/core/factories/MetadataTypeTagFactory.go +97 -24
- package/native/core/factories/MetadataTypeTagSchemaFactory.go +5 -0
- package/native/core/factories/expression_factory_number_spells_non_finite_values_test.go +69 -0
- package/native/core/factories/internal/metadata/IMetadataIteratorProps.go +6 -2
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +49 -15
- package/native/core/factories/literal_factory_null_survives_objects_test.go +43 -0
- package/native/core/factories/metadata_comment_tag_factory_holds_bigint_tags_exact_test.go +168 -0
- package/native/core/factories/metadata_comment_tag_factory_reads_javascript_numbers_test.go +158 -0
- package/native/core/factories/metadata_comment_tag_factory_string_length_helpers_test.go +55 -0
- package/native/core/factories/metadata_type_tag_factory_reports_all_invalid_candidates_test.go +73 -0
- package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +1 -3
- package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +107 -9
- package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +1 -3
- package/native/core/programmers/http/HttpFormDataProgrammer.go +41 -2
- package/native/core/programmers/http/HttpHeadersProgrammer.go +56 -4
- package/native/core/programmers/http/HttpParameterProgrammer.go +1 -3
- package/native/core/programmers/http/HttpQueryProgrammer.go +26 -43
- package/native/core/programmers/iterate/json_schema_jsDocTags.go +10 -3
- package/native/core/programmers/iterate/json_schema_jsdoc_tags_cast_reads_javascript_numbers_test.go +64 -0
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +1 -0
- package/native/core/programmers/llm/LlmEvaluationProgrammer.go +559 -0
- package/native/core/programmers/llm/LlmParametersProgrammer.go +6 -5
- package/native/core/programmers/llm/LlmReferenceDescription.go +113 -0
- package/native/core/programmers/llm/LlmSchemaProgrammer.go +34 -4
- package/native/core/programmers/llm/llm_reference_description_test.go +134 -0
- package/native/core/schemas/metadata/MetadataDependency.go +9 -0
- package/native/core/utils/NumberUtil.go +195 -0
- package/native/core/utils/number_util_rational_keeps_written_value_test.go +62 -0
- package/native/core/utils/number_util_reads_javascript_number_grammar_test.go +147 -0
- package/native/transform/CallExpressionTransformer.go +1 -0
- package/native/transform/FileTransformer.go +5 -0
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations.go +94 -0
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations_test.go +50 -0
- package/native/transform/features/llm/LlmEvaluationTransformer.go +59 -0
- package/native/transform/features/llm/LlmSchemaTransformer.go +14 -11
- package/native/transform/features/llm/llm_transformer_entry_coverage_test.go +29 -27
- package/package.json +3 -3
- package/src/http.ts +61 -34
- package/src/internal/_createLlmEvaluation.ts +678 -0
- package/src/internal/_httpFormDataReadBigint.ts +3 -1
- package/src/internal/_httpFormDataReadBoolean.ts +3 -1
- package/src/internal/_httpFormDataReadNumber.ts +3 -1
- package/src/internal/_httpFormDataReadString.ts +10 -1
- package/src/internal/_httpHeaderReadBigint.ts +6 -1
- package/src/internal/_httpHeaderReadNumber.ts +6 -1
- package/src/internal/_httpParameterReadBigint.ts +3 -0
- package/src/internal/_httpParameterReadNumber.ts +3 -0
- package/src/internal/_httpParameterReadString.ts +12 -2
- package/src/internal/_httpQueryReadBigint.ts +7 -1
- package/src/internal/_httpQueryReadBoolean.ts +3 -1
- package/src/internal/_httpQueryReadNumber.ts +7 -1
- package/src/internal/_httpQueryReadString.ts +10 -1
- package/src/internal/_randomBigint.ts +111 -3
- package/src/internal/_randomInteger.ts +31 -16
- package/src/internal/_randomNumber.ts +23 -12
- package/src/internal/_stringLengthGte.ts +6 -0
- package/src/internal/_stringLengthLte.ts +6 -0
- package/src/llm.ts +64 -0
- package/src/re-exports.ts +1 -0
|
@@ -7,6 +7,7 @@ import (
|
|
|
7
7
|
"io"
|
|
8
8
|
"os"
|
|
9
9
|
"path/filepath"
|
|
10
|
+
"sync"
|
|
10
11
|
|
|
11
12
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
12
13
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
@@ -114,6 +115,7 @@ func runBuild(args []string) int {
|
|
|
114
115
|
transformDiags := []typiaTransformDiagnostic{}
|
|
115
116
|
transformOptions := pluginOptions.TransformOptions()
|
|
116
117
|
extras := nativecontext.ITypiaContext_Extras{
|
|
118
|
+
Shared: &sync.Map{},
|
|
117
119
|
AddDiagnostic: func(diag *nativecontext.ITypiaDiagnostic) int {
|
|
118
120
|
transformDiags = append(transformDiags, typiaTransformDiagnosticFrom(diag))
|
|
119
121
|
return len(transformDiags)
|
|
@@ -54,7 +54,7 @@ func TestDynamicKeyTagsTransform(t *testing.T) {
|
|
|
54
54
|
}
|
|
55
55
|
// The key is read into a local before it is tested, so its predicate is the
|
|
56
56
|
// one place the emitted text can show the check exists at all.
|
|
57
|
-
if !strings.Contains(out, "RegExp(") || !strings.Contains(out, "
|
|
57
|
+
if !strings.Contains(out, "RegExp(") || !strings.Contains(out, "_stringLengthGte") {
|
|
58
58
|
t.Fatalf("emitted checker should test the dynamic key, not only its value:\n%s", out)
|
|
59
59
|
}
|
|
60
60
|
dynamicKeyTagsRunRuntimeCases(t, project, out)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
// TestLlmEvaluationAcceptsPropertyAndEnumMemberProbability verifies both
|
|
6
|
+
// supported JSDoc placements compile with the upper probability boundary.
|
|
7
|
+
//
|
|
8
|
+
// Primitive aliases may erase their name, but a final decision property's
|
|
9
|
+
// comment and enum members' comments remain attached to their declarations.
|
|
10
|
+
//
|
|
11
|
+
// 1. Declare an aliased boolean and a probability-annotated enum.
|
|
12
|
+
// 2. Put probability one on the final boolean decision property.
|
|
13
|
+
// 3. Require successful compilation.
|
|
14
|
+
func TestLlmEvaluationAcceptsPropertyAndEnumMemberProbability(t *testing.T) {
|
|
15
|
+
llmEvaluationAccepts(t, "property-probability-valid", `import typia from "typia";
|
|
16
|
+
|
|
17
|
+
type Urgency = boolean;
|
|
18
|
+
enum Choice {
|
|
19
|
+
/** @probability 1 */ yes = "yes",
|
|
20
|
+
/** @probability 0.2 */ no = "no",
|
|
21
|
+
}
|
|
22
|
+
typia.llm.evaluation<{
|
|
23
|
+
/** Urgent? @probability 1 */ urgent: Urgency;
|
|
24
|
+
/** Choice? */ choice: Choice;
|
|
25
|
+
}>();
|
|
26
|
+
`)
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
// TestLlmEvaluationAcceptsUnselectedSourceProbability verifies a comment on
|
|
6
|
+
// an unrelated property does not constrain an indexed-access decision.
|
|
7
|
+
//
|
|
8
|
+
// Indexed access extracts a property type, not its JSDoc. An annotation on a
|
|
9
|
+
// different source property must not reject an otherwise valid evaluation.
|
|
10
|
+
//
|
|
11
|
+
// 1. Declare an annotated and an unannotated source property.
|
|
12
|
+
// 2. Extract the unannotated property's type for an evaluation decision.
|
|
13
|
+
// 3. Require successful compilation.
|
|
14
|
+
func TestLlmEvaluationAcceptsUnselectedSourceProbability(t *testing.T) {
|
|
15
|
+
llmEvaluationAccepts(t, "unselected-source-probability", `import typia from "typia";
|
|
16
|
+
interface Source {
|
|
17
|
+
/** @probability 0.8 */ ignored: boolean;
|
|
18
|
+
selected: boolean;
|
|
19
|
+
}
|
|
20
|
+
type Value = Source["selected"];
|
|
21
|
+
typia.llm.evaluation<{
|
|
22
|
+
/** Is it urgent? */
|
|
23
|
+
urgent: Value;
|
|
24
|
+
}>();
|
|
25
|
+
`)
|
|
26
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// Evaluation promises to decode the entire T. Every required member must
|
|
9
|
+
// become a question or fail compilation, including inherited/nested members.
|
|
10
|
+
func TestLlmEvaluationCompleteObjectShape(t *testing.T) {
|
|
11
|
+
accepted := map[string]string{
|
|
12
|
+
"getter only": `class Decision {
|
|
13
|
+
/** Is it urgent? @probability 1 */ get urgent(): boolean { return true; }
|
|
14
|
+
}`,
|
|
15
|
+
"class getter": `class Decision {
|
|
16
|
+
/** Is it active? */ active!: boolean;
|
|
17
|
+
/** Is it urgent? @probability 1 */ get urgent(): boolean { return true; }
|
|
18
|
+
}`,
|
|
19
|
+
"inherited getter": `class Base {
|
|
20
|
+
/** Is it urgent? @probability 1 */ get urgent(): boolean { return true; }
|
|
21
|
+
}
|
|
22
|
+
class Decision extends Base {
|
|
23
|
+
/** Is it active? */
|
|
24
|
+
active!: boolean;
|
|
25
|
+
}`,
|
|
26
|
+
"interface getter": `interface Decision {
|
|
27
|
+
/** Is it active? */ active: boolean;
|
|
28
|
+
/** Is it urgent? @probability 1 */ get urgent(): boolean;
|
|
29
|
+
}`,
|
|
30
|
+
"nested getter": `class Child {
|
|
31
|
+
/** Is it urgent? @probability 1 */ get urgent(): boolean { return true; }
|
|
32
|
+
}
|
|
33
|
+
interface Decision { child: Child; }`,
|
|
34
|
+
"setter": `class Decision {
|
|
35
|
+
/** Is it active? */ active!: boolean;
|
|
36
|
+
/** Is it urgent? @probability 1 */ set urgent(value: boolean) {}
|
|
37
|
+
}`,
|
|
38
|
+
}
|
|
39
|
+
for name, declaration := range accepted {
|
|
40
|
+
t.Run(name, func(t *testing.T) {
|
|
41
|
+
source := `import typia from "typia";` + declaration + `
|
|
42
|
+
export const evaluation = typia.llm.evaluation<Decision>();`
|
|
43
|
+
project := llmEvaluationProject(t, "complete-"+strings.ReplaceAll(name, " ", "-"), source)
|
|
44
|
+
out, diagnostic, code := ttscTypiaTestCapture(func() int {
|
|
45
|
+
return runTransform([]string{"--cwd", project, "--tsconfig", "tsconfig.json", "--file", "src/main.ts", "--output", "js"})
|
|
46
|
+
})
|
|
47
|
+
if code != 0 || !strings.Contains(out, "threshold: 1") || !strings.Contains(out, `"urgent"`) {
|
|
48
|
+
t.Fatalf("getter is not in the plan: code=%d threshold=%t urgent=%t diagnostic=%s", code, strings.Contains(out, "threshold: 1"), strings.Contains(out, `"urgent"`), diagnostic)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
rejected := map[string]struct{ declaration, diagnostic string }{
|
|
54
|
+
"method": {`interface Decision { /** Is it active? */ active: boolean; execute(): void; }`, "function"},
|
|
55
|
+
"private": {`class Decision { /** Is it active? */ active!: boolean; private token = 1; }`, "private"},
|
|
56
|
+
"protected": {`class Decision { /** Is it active? */ active!: boolean; protected token = 1; }`, "protected"},
|
|
57
|
+
"sharp private": {`class Decision { /** Is it active? */ active!: boolean; #token = 1; }`, "private"},
|
|
58
|
+
"symbol": {`declare const token: unique symbol; interface Decision { /** Is it active? */ active: boolean; [token]: boolean; }`, "symbol-keyed"},
|
|
59
|
+
"symbol index": {`interface Decision { /** Is it active? */ active: boolean; [key: symbol]: boolean; }`, "symbol-keyed"},
|
|
60
|
+
"call": {`interface Decision { /** Is it active? */ active: boolean; (): void; }`, "callable"},
|
|
61
|
+
"construct": {`interface Decision { /** Is it active? */ active: boolean; new(): Decision; }`, "constructable"},
|
|
62
|
+
"nested method": {`interface Child { /** Is it urgent? */ urgent: boolean; execute(): void; }
|
|
63
|
+
interface Decision { child: Child; }`, "function"},
|
|
64
|
+
"inherited private": {`class Base { private token = 1; }
|
|
65
|
+
class Decision extends Base { /** Is it active? */ active!: boolean; }`, "private"},
|
|
66
|
+
"generic private": {`class Base<T> { private token!: T; }
|
|
67
|
+
class Decision extends Base<boolean> { /** Is it active? */ active!: boolean; }`, "private"},
|
|
68
|
+
"generic sharp private": {`class Base<T> { #token!: T; }
|
|
69
|
+
class Decision extends Base<boolean> { /** Is it active? */ active!: boolean; }`, "private"},
|
|
70
|
+
"interface extends private class": {`class Base { private token!: boolean; }
|
|
71
|
+
interface Decision extends Base { active: boolean; }`, "private"},
|
|
72
|
+
"internal": {`interface Decision {
|
|
73
|
+
/** Is it active? */ active: boolean;
|
|
74
|
+
/** @internal */ secret: boolean;
|
|
75
|
+
}`, "hidden properties"},
|
|
76
|
+
"intersection internal": {`type Decision = {
|
|
77
|
+
/** Is it active? */ active: boolean;
|
|
78
|
+
} & {
|
|
79
|
+
/** @internal */ secret: boolean;
|
|
80
|
+
};`, "hidden properties"},
|
|
81
|
+
"overlapping intersection internal": {`type Decision = {
|
|
82
|
+
/** Is it active? */ active: boolean;
|
|
83
|
+
/** @internal */ secret: boolean;
|
|
84
|
+
} & { secret: boolean };`, "hidden properties"},
|
|
85
|
+
}
|
|
86
|
+
for name, testcase := range rejected {
|
|
87
|
+
t.Run(name, func(t *testing.T) {
|
|
88
|
+
source := `import typia from "typia";` + testcase.declaration + `
|
|
89
|
+
export const evaluation = typia.llm.evaluation<Decision>();`
|
|
90
|
+
diagnostic := llmEvaluationDiagnosticsBuild(t, "complete-"+strings.ReplaceAll(name, " ", "-"), source)
|
|
91
|
+
if !strings.Contains(diagnostic, testcase.diagnostic) {
|
|
92
|
+
t.Fatalf("missing %q diagnostic: %s", testcase.diagnostic, diagnostic)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
package/native/cmd/ttsc-typia/llm_evaluation_ignores_declaration_probability_without_call_test.go
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
// TestLlmEvaluationIgnoresDeclarationProbabilityWithoutCall verifies a
|
|
6
|
+
// program without evaluation calls does not trigger JEV-specific diagnostics.
|
|
7
|
+
//
|
|
8
|
+
// The placement scanner is owned by the evaluation transformer, so merely
|
|
9
|
+
// declaring a JSDoc tag must not alter ordinary TypeScript compilation.
|
|
10
|
+
//
|
|
11
|
+
// 1. Declare a tagged primitive alias.
|
|
12
|
+
// 2. Omit every evaluation call.
|
|
13
|
+
// 3. Require successful compilation.
|
|
14
|
+
func TestLlmEvaluationIgnoresDeclarationProbabilityWithoutCall(t *testing.T) {
|
|
15
|
+
llmEvaluationAccepts(t, "no-evaluation-call", `/** @probability 0.8 */
|
|
16
|
+
export type Urgency = boolean;
|
|
17
|
+
`)
|
|
18
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestLlmEvaluationPlanEmissionTransform verifies typia.llm.evaluation emits
|
|
9
|
+
// one deterministic plan call with the documented description sources.
|
|
10
|
+
//
|
|
11
|
+
// The transform replaces the call with `_createLlmEvaluation(plan)`, whose
|
|
12
|
+
// plan carries every question's text. A property or enum member `@description`
|
|
13
|
+
// tag overrides its summary, as it does in typia's JSON schemas, and repository
|
|
14
|
+
// formatting rewrites that tag in TypeScript sources, so this fixture pins it
|
|
15
|
+
// here. The same source must also emit byte-identical output on every run,
|
|
16
|
+
// because the plan feeds build caches and published bundles.
|
|
17
|
+
//
|
|
18
|
+
// 1. Transform a decision type with `@description` overrides on a property and
|
|
19
|
+
// an enum member, a threshold, and a member minimum.
|
|
20
|
+
// 2. Transform the same project again.
|
|
21
|
+
// 3. Assert the runtime helper call, the overridden texts, the requirements,
|
|
22
|
+
// and identical output across runs.
|
|
23
|
+
func TestLlmEvaluationPlanEmissionTransform(t *testing.T) {
|
|
24
|
+
project := llmEvaluationProject(t, "emission", llmEvaluationEmissionSource)
|
|
25
|
+
transform := func() string {
|
|
26
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
27
|
+
return runTransform([]string{
|
|
28
|
+
"--cwd", project,
|
|
29
|
+
"--tsconfig", "tsconfig.json",
|
|
30
|
+
"--file", "src/main.ts",
|
|
31
|
+
"--output", "js",
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
if code != 0 {
|
|
35
|
+
t.Fatalf("llm.evaluation transform failed: code=%d stderr=\n%s", code, errText)
|
|
36
|
+
}
|
|
37
|
+
return out
|
|
38
|
+
}
|
|
39
|
+
first := transform()
|
|
40
|
+
for _, expected := range []string{
|
|
41
|
+
"_createLlmEvaluation",
|
|
42
|
+
`instructions: "Overridden question"`,
|
|
43
|
+
`description: "Overridden member"`,
|
|
44
|
+
`description: "Plain member"`,
|
|
45
|
+
"threshold: 0.8",
|
|
46
|
+
"minimum: 0.75",
|
|
47
|
+
} {
|
|
48
|
+
if !strings.Contains(first, expected) {
|
|
49
|
+
t.Fatalf("llm.evaluation emission missing %q:\n%s", expected, first)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for _, unexpected := range []string{`instructions: "Summary`, `description: "Summary`, "typia.llm.evaluation"} {
|
|
53
|
+
if strings.Contains(first, unexpected) {
|
|
54
|
+
t.Fatalf("llm.evaluation emission must not contain %q:\n%s", unexpected, first)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if second := transform(); second != first {
|
|
58
|
+
t.Fatalf("llm.evaluation emission is not deterministic:\n--- first\n%s\n--- second\n%s", first, second)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const llmEvaluationEmissionSource = `import typia, { tags } from "typia";
|
|
63
|
+
|
|
64
|
+
enum Team {
|
|
65
|
+
/**
|
|
66
|
+
* Plain member
|
|
67
|
+
*
|
|
68
|
+
* @probability 0.5
|
|
69
|
+
*/
|
|
70
|
+
billing = "billing",
|
|
71
|
+
/**
|
|
72
|
+
* Summary to be overridden
|
|
73
|
+
*
|
|
74
|
+
* @description Overridden member
|
|
75
|
+
* @probability 0.75
|
|
76
|
+
*/
|
|
77
|
+
technical = "technical",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const evaluation = typia.llm.evaluation<{
|
|
81
|
+
/**
|
|
82
|
+
* Summary to be overridden
|
|
83
|
+
*
|
|
84
|
+
* @description Overridden question
|
|
85
|
+
*/
|
|
86
|
+
team: Team;
|
|
87
|
+
/** Is a refund requested? */
|
|
88
|
+
refund: boolean & tags.Probability<0.8>;
|
|
89
|
+
}>();
|
|
90
|
+
`
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"slices"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// TestLlmEvaluationProgramScanDependencies verifies a successful evaluation
|
|
13
|
+
// transform watches source files used by its program-wide placement check.
|
|
14
|
+
//
|
|
15
|
+
// An unrelated source has no tag today but may gain an illegal declaration
|
|
16
|
+
// tag tomorrow. A complete dependency list must then invalidate the old emit.
|
|
17
|
+
//
|
|
18
|
+
// 1. Transform a decision beside an unrelated, untagged source module.
|
|
19
|
+
// 2. Require the decision's complete dependency list to include that module.
|
|
20
|
+
// 3. Add an illegal tag in that module and require a new diagnostic.
|
|
21
|
+
func TestLlmEvaluationProgramScanDependencies(t *testing.T) {
|
|
22
|
+
dir := llmEvaluationProject(t, "program-scan-dependencies", `import typia from "typia";
|
|
23
|
+
interface IDecision {
|
|
24
|
+
/** Is it urgent? */
|
|
25
|
+
urgent: boolean;
|
|
26
|
+
}
|
|
27
|
+
typia.llm.evaluation<IDecision>();
|
|
28
|
+
`)
|
|
29
|
+
unused := `export type Unused = boolean;
|
|
30
|
+
`
|
|
31
|
+
if err := os.WriteFile(filepath.Join(dir, "src", "unused.ts"), []byte(unused), 0o644); err != nil {
|
|
32
|
+
t.Fatal(err)
|
|
33
|
+
}
|
|
34
|
+
output, diagnostics, code := ttscTypiaTestCapture(func() int {
|
|
35
|
+
return runTransform([]string{"--cwd", dir, "--tsconfig", "tsconfig.json"})
|
|
36
|
+
})
|
|
37
|
+
var result transformProjectOutput
|
|
38
|
+
if err := json.Unmarshal([]byte(output), &result); err != nil {
|
|
39
|
+
t.Fatal(err)
|
|
40
|
+
}
|
|
41
|
+
if code != 0 {
|
|
42
|
+
t.Fatalf("valid project must transform: code=%d\ndiagnostics=%+v\nstderr=%s", code, result.Diagnostics, diagnostics)
|
|
43
|
+
}
|
|
44
|
+
if !slices.Contains(result.DependenciesComplete, "src/main.ts") {
|
|
45
|
+
t.Fatalf("decision transform unexpectedly lacks complete dependencies: %+v", result.DependenciesComplete)
|
|
46
|
+
}
|
|
47
|
+
if !slices.Contains(result.Dependencies["src/main.ts"], "src/unused.ts") {
|
|
48
|
+
t.Fatalf("program-wide scan dependency is missing: %+v", result.Dependencies["src/main.ts"])
|
|
49
|
+
}
|
|
50
|
+
tagged := `/** @probability 0.8 */
|
|
51
|
+
export type Unused = boolean;
|
|
52
|
+
`
|
|
53
|
+
if err := os.WriteFile(filepath.Join(dir, "src", "unused.ts"), []byte(tagged), 0o644); err != nil {
|
|
54
|
+
t.Fatal(err)
|
|
55
|
+
}
|
|
56
|
+
output, diagnostics, code = ttscTypiaTestCapture(func() int {
|
|
57
|
+
return runTransform([]string{"--cwd", dir, "--tsconfig", "tsconfig.json"})
|
|
58
|
+
})
|
|
59
|
+
if code != 3 {
|
|
60
|
+
t.Fatalf("new invalid tag must fail: code=%d\nstderr=%s", code, diagnostics)
|
|
61
|
+
}
|
|
62
|
+
result = transformProjectOutput{}
|
|
63
|
+
if err := json.Unmarshal([]byte(output), &result); err != nil {
|
|
64
|
+
t.Fatal(err)
|
|
65
|
+
}
|
|
66
|
+
if len(result.Diagnostics) != 1 || !strings.Contains(result.Diagnostics[0].MessageText, "type alias Unused") {
|
|
67
|
+
t.Fatalf("changed source must affect evaluation diagnostic: %+v", result.Diagnostics)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestLlmEvaluationRejectsDeclarationProbability verifies JSDoc on a type
|
|
9
|
+
// declaration cannot silently become a decision requirement.
|
|
10
|
+
//
|
|
11
|
+
// A primitive alias resolves to the same primitive as an unannotated alias,
|
|
12
|
+
// so the compiler must reject unsupported declaration-level placements.
|
|
13
|
+
//
|
|
14
|
+
// 1. Annotate several declaration kinds, including unused declarations.
|
|
15
|
+
// 2. Compile a neighboring evaluation call.
|
|
16
|
+
// 3. Require a normal diagnostic for every invalid placement.
|
|
17
|
+
func TestLlmEvaluationRejectsDeclarationProbability(t *testing.T) {
|
|
18
|
+
diagnostics := llmEvaluationDiagnosticsBuild(t, "declaration-probability", `import typia from "typia";
|
|
19
|
+
|
|
20
|
+
/** @probability 0.8 */
|
|
21
|
+
type Urgency = boolean;
|
|
22
|
+
/** @probability invalid */
|
|
23
|
+
type Unused = boolean;
|
|
24
|
+
/** @probability 0.6 */
|
|
25
|
+
interface Nested { /** Answer? */ answer: boolean }
|
|
26
|
+
/** @probability 0.7 */
|
|
27
|
+
enum Choice { yes = "yes", no = "no" }
|
|
28
|
+
/** @probability 0.4 */
|
|
29
|
+
class Container { /** Answer? */ answer = true; }
|
|
30
|
+
/** @probability 0.3 */
|
|
31
|
+
function unrelated(): boolean { return true; }
|
|
32
|
+
|
|
33
|
+
typia.llm.evaluation<{
|
|
34
|
+
/** Urgent? */ urgent: Urgency;
|
|
35
|
+
/** Nested? */ nested: Nested;
|
|
36
|
+
/** Choice? */ choice: Choice;
|
|
37
|
+
}>();
|
|
38
|
+
`)
|
|
39
|
+
for _, name := range []string{"type alias Urgency", "type alias Unused", "interface Nested", "enum Choice", "class Container", "declaration unrelated"} {
|
|
40
|
+
if !strings.Contains(diagnostics, name) {
|
|
41
|
+
t.Fatalf("misplaced declaration tag %q was not diagnosed:\n%s", name, diagnostics)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestLlmEvaluationRejectsAliasRefinement verifies a named alias cannot hide
|
|
9
|
+
// a validation tag from the evaluation compiler.
|
|
10
|
+
//
|
|
11
|
+
// An alias of a boolean refinement still denotes a constrained boolean. The
|
|
12
|
+
// decoder does not enforce that constraint, while an annotation-only alias is
|
|
13
|
+
// safe and must remain accepted.
|
|
14
|
+
//
|
|
15
|
+
// 1. Declare a validation refinement behind a named alias.
|
|
16
|
+
// 2. Require a decision-path diagnostic.
|
|
17
|
+
// 3. Accept a neighboring annotation-only alias.
|
|
18
|
+
func TestLlmEvaluationRejectsAliasRefinement(t *testing.T) {
|
|
19
|
+
errText := llmEvaluationDiagnosticsBuild(t, "alias-refinement", `import typia, { tags } from "typia";
|
|
20
|
+
|
|
21
|
+
type TrueOnly = tags.TagBase<{
|
|
22
|
+
target: "boolean";
|
|
23
|
+
kind: "trueOnly";
|
|
24
|
+
value: undefined;
|
|
25
|
+
validate: "$input === true";
|
|
26
|
+
}>;
|
|
27
|
+
type Guarded = boolean & TrueOnly;
|
|
28
|
+
typia.llm.evaluation<{
|
|
29
|
+
/** Is it allowed? */
|
|
30
|
+
allowed: Guarded;
|
|
31
|
+
}>();
|
|
32
|
+
`)
|
|
33
|
+
expected := "- $input.allowed\n - LLM evaluation does not support type tag \"trueOnly\", because decode() cannot enforce its constraint."
|
|
34
|
+
if !strings.Contains(errText, expected) {
|
|
35
|
+
t.Fatalf("missing aliased refinement diagnostic:\n%s", errText)
|
|
36
|
+
}
|
|
37
|
+
llmEvaluationAccepts(t, "alias-refinement-control", `import typia, { tags } from "typia";
|
|
38
|
+
|
|
39
|
+
type Annotated = boolean & tags.Default<true>;
|
|
40
|
+
typia.llm.evaluation<{
|
|
41
|
+
/** Is it allowed? */
|
|
42
|
+
allowed: Annotated;
|
|
43
|
+
}>();
|
|
44
|
+
`)
|
|
45
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestLlmEvaluationRejectsComputedMethodProbability verifies an unsupported
|
|
9
|
+
// computed method annotation produces a diagnostic instead of a panic.
|
|
10
|
+
//
|
|
11
|
+
// A computed property name is not a text-bearing identifier in the parser.
|
|
12
|
+
// Reading its name as plain text used to crash the compiler's diagnostic path.
|
|
13
|
+
//
|
|
14
|
+
// 1. Annotate a computed class method with @probability.
|
|
15
|
+
// 2. Build an otherwise valid evaluation call.
|
|
16
|
+
// 3. Require a normal transform diagnostic.
|
|
17
|
+
func TestLlmEvaluationRejectsComputedMethodProbability(t *testing.T) {
|
|
18
|
+
diagnostics := llmEvaluationDiagnosticsBuild(t, "computed-method-probability", `import typia from "typia";
|
|
19
|
+
class Source {
|
|
20
|
+
/** @probability 0.8 */ [Symbol.iterator]() { return []; }
|
|
21
|
+
}
|
|
22
|
+
typia.llm.evaluation<{
|
|
23
|
+
/** Is it urgent? */
|
|
24
|
+
urgent: boolean;
|
|
25
|
+
}>();
|
|
26
|
+
`)
|
|
27
|
+
if !strings.Contains(diagnostics, "@probability") {
|
|
28
|
+
t.Fatalf("computed method tag must produce a diagnostic:\n%s", diagnostics)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"strings"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestLlmEvaluationRejectsImportedDeclarationProbability verifies the
|
|
11
|
+
// placement diagnostic crosses an imported source-file boundary.
|
|
12
|
+
//
|
|
13
|
+
// A local-only syntax scan would miss an alias declared in another file while
|
|
14
|
+
// the checker still resolves that alias at the evaluation decision.
|
|
15
|
+
//
|
|
16
|
+
// 1. Define an annotated primitive alias in an imported module.
|
|
17
|
+
// 2. Use it in evaluation calls in two source files.
|
|
18
|
+
// 3. Require the imported declaration's diagnostic from both CLI paths.
|
|
19
|
+
func TestLlmEvaluationRejectsImportedDeclarationProbability(t *testing.T) {
|
|
20
|
+
dir := llmEvaluationProject(t, "imported-declaration-probability", `import typia from "typia";
|
|
21
|
+
import type { Urgency } from "./urgency";
|
|
22
|
+
typia.llm.evaluation<{ /** Urgent? */ urgent: Urgency }>();
|
|
23
|
+
`)
|
|
24
|
+
source := `/** @probability 0.8 */
|
|
25
|
+
export type Urgency = boolean;
|
|
26
|
+
`
|
|
27
|
+
if err := os.WriteFile(filepath.Join(dir, "src", "urgency.ts"), []byte(source), 0o644); err != nil {
|
|
28
|
+
t.Fatal(err)
|
|
29
|
+
}
|
|
30
|
+
second := `import typia from "typia";
|
|
31
|
+
typia.llm.evaluation<{ /** Is it urgent? */ urgent: boolean }>();
|
|
32
|
+
`
|
|
33
|
+
if err := os.WriteFile(filepath.Join(dir, "src", "second.ts"), []byte(second), 0o644); err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
_, diagnostics, code := ttscTypiaTestCapture(func() int {
|
|
37
|
+
return runBuild([]string{"--cwd", dir, "--tsconfig", "tsconfig.json", "--emit", "--outDir", filepath.Join(dir, "dist")})
|
|
38
|
+
})
|
|
39
|
+
if code != 3 || strings.Count(diagnostics, "type alias Urgency") != 2 {
|
|
40
|
+
t.Fatalf("imported declaration tag must fail in both files: code=%d\n%s", code, diagnostics)
|
|
41
|
+
}
|
|
42
|
+
output, errText, code := ttscTypiaTestCapture(func() int {
|
|
43
|
+
return runTransform([]string{"--cwd", dir, "--tsconfig", "tsconfig.json"})
|
|
44
|
+
})
|
|
45
|
+
if code != 3 || strings.Count(output, "type alias Urgency") != 2 {
|
|
46
|
+
t.Fatalf("transform path must diagnose both files: code=%d\nstdout=%s\nstderr=%s", code, output, errText)
|
|
47
|
+
}
|
|
48
|
+
}
|