typia 14.0.6 → 15.0.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/README.md +3 -0
- 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/_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 +199 -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/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/IdentifierFactory.go +10 -0
- package/native/core/factories/LiteralFactory.go +16 -0
- package/native/core/factories/MetadataCommentTagFactory.go +12 -10
- package/native/core/factories/MetadataTypeTagFactory.go +97 -24
- package/native/core/factories/MetadataTypeTagSchemaFactory.go +5 -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_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/llm/LlmApplicationProgrammer.go +1 -0
- package/native/core/programmers/llm/LlmEvaluationProgrammer.go +555 -0
- package/native/core/programmers/llm/LlmParametersProgrammer.go +6 -5
- package/native/core/programmers/llm/LlmReferenceDescription.go +113 -0
- package/native/core/programmers/llm/llm_reference_description_test.go +134 -0
- package/native/core/schemas/metadata/MetadataDependency.go +9 -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/internal/_createLlmEvaluation.ts +678 -0
- 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
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// llmReferenceDescription_root describes a parameters root that was reached
|
|
10
|
+
// through `$ref`, the way `LlmSchemaConverter.parameters` does with
|
|
11
|
+
// `JsonDescriptor.cascade({ escape: true })`: the type's own description, then
|
|
12
|
+
// each described namespace parent quoted, or `Current Type: {@link Name}` when
|
|
13
|
+
// the type carries none (samchon/typia#2405). A root that is not a reference
|
|
14
|
+
// keeps `own`.
|
|
15
|
+
func llmReferenceDescription_root(
|
|
16
|
+
schema nativeiterate.JsonSchema,
|
|
17
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
18
|
+
own *string,
|
|
19
|
+
) *string {
|
|
20
|
+
ref, ok := schema["$ref"].(string)
|
|
21
|
+
if ok == false {
|
|
22
|
+
return own
|
|
23
|
+
}
|
|
24
|
+
key, ok := llmReferenceDescription_key(ref)
|
|
25
|
+
if ok == false {
|
|
26
|
+
return own
|
|
27
|
+
}
|
|
28
|
+
return llmReferenceDescription_cascade(key, components, own)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// llmReferenceDescription_cascade ports `JsonDescriptor.cascade` with
|
|
32
|
+
// `escape: true`.
|
|
33
|
+
func llmReferenceDescription_cascade(
|
|
34
|
+
key string,
|
|
35
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
36
|
+
own *string,
|
|
37
|
+
) *string {
|
|
38
|
+
type parent struct {
|
|
39
|
+
key string
|
|
40
|
+
description *string
|
|
41
|
+
}
|
|
42
|
+
accessors := strings.Split(key, ".")
|
|
43
|
+
parents := []parent{}
|
|
44
|
+
for i := len(accessors); i >= 1; i-- {
|
|
45
|
+
current := strings.Join(accessors[:i], ".")
|
|
46
|
+
var description *string
|
|
47
|
+
if components != nil && components.Schemas != nil {
|
|
48
|
+
if target, found := components.Schemas[current]; found {
|
|
49
|
+
if text, ok := target["description"].(string); ok {
|
|
50
|
+
description = &text
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// the type itself always stays; a parent only when it is described
|
|
55
|
+
if i == len(accessors) || (description != nil && *description != "") {
|
|
56
|
+
parents = append(parents, parent{key: current, description: description})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (own == nil || *own == "") && len(parents) == 0 {
|
|
60
|
+
return nil
|
|
61
|
+
}
|
|
62
|
+
parts := []string{}
|
|
63
|
+
if own != nil && *own != "" {
|
|
64
|
+
parts = append(parts, *own)
|
|
65
|
+
}
|
|
66
|
+
for i, p := range parents {
|
|
67
|
+
if p.description == nil {
|
|
68
|
+
parts = append(parts, "Current Type: {@link "+p.key+"}")
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
role := "parent"
|
|
72
|
+
if i == 0 {
|
|
73
|
+
role = "current"
|
|
74
|
+
}
|
|
75
|
+
lines := strings.Split(*p.description, "\n")
|
|
76
|
+
for j, line := range lines {
|
|
77
|
+
lines[j] = "> " + line
|
|
78
|
+
}
|
|
79
|
+
parts = append(parts, "Description of the "+role+" {@link "+p.key+"} type:\n\n"+strings.Join(lines, "\n"))
|
|
80
|
+
}
|
|
81
|
+
output := strings.Join(parts, "\n\n------------------------------\n\n")
|
|
82
|
+
return &output
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// llmReferenceDescription_key reads the component key of a
|
|
86
|
+
// `#/components/schemas/` reference. Native references carry the raw key, the
|
|
87
|
+
// one `components.schemas` is indexed by; `LlmReference.readOpenApi` decodes
|
|
88
|
+
// the escaped form of the same key in an emitted document.
|
|
89
|
+
func llmReferenceDescription_key(ref string) (string, bool) {
|
|
90
|
+
const prefix = "#/components/schemas/"
|
|
91
|
+
if strings.HasPrefix(ref, prefix) == false {
|
|
92
|
+
return "", false
|
|
93
|
+
}
|
|
94
|
+
return strings.TrimPrefix(ref, prefix), true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// llmReferenceDescription_assign sets the cascaded root description on a
|
|
98
|
+
// converted parameters object, removing it when the cascade yields none.
|
|
99
|
+
func llmReferenceDescription_assign(
|
|
100
|
+
output map[string]any,
|
|
101
|
+
schema nativeiterate.JsonSchema,
|
|
102
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
103
|
+
) {
|
|
104
|
+
var own *string
|
|
105
|
+
if text, ok := output["description"].(string); ok {
|
|
106
|
+
own = &text
|
|
107
|
+
}
|
|
108
|
+
if description := llmReferenceDescription_root(schema, components, own); description != nil {
|
|
109
|
+
output["description"] = *description
|
|
110
|
+
} else {
|
|
111
|
+
delete(output, "description")
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestLlmReferenceDescription locks the root description of a parameters
|
|
10
|
+
// object reached through `$ref` to `JsonDescriptor.cascade({ escape: true })`,
|
|
11
|
+
// the description `LlmSchemaConverter.parameters` writes for the same
|
|
12
|
+
// collection (samchon/typia#2405).
|
|
13
|
+
func TestLlmReferenceDescription(t *testing.T) {
|
|
14
|
+
t.Parallel()
|
|
15
|
+
const separator = "\n\n------------------------------\n\n"
|
|
16
|
+
text := func(value string) *string { return &value }
|
|
17
|
+
components := &nativeiterate.OpenApi_IComponents{
|
|
18
|
+
Schemas: map[string]nativeiterate.JsonSchema{
|
|
19
|
+
"IPlain": {"type": "object"},
|
|
20
|
+
"IMember": {"type": "object", "description": "Member.\nSecond line."},
|
|
21
|
+
"IMember.ICreate": {"type": "object", "description": "Creation."},
|
|
22
|
+
"IMember.IBare": {"type": "object"},
|
|
23
|
+
"IMember.Deep": {"type": "object"},
|
|
24
|
+
"IMember.Deep.ILeaf": {"type": "object"},
|
|
25
|
+
"IMember.IEmpty": {"type": "object", "description": ""},
|
|
26
|
+
"INamespaceless.IItem": {"type": "object"},
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
cases := []struct {
|
|
30
|
+
name string
|
|
31
|
+
schema nativeiterate.JsonSchema
|
|
32
|
+
own *string
|
|
33
|
+
expected *string
|
|
34
|
+
}{
|
|
35
|
+
{
|
|
36
|
+
name: "undescribed",
|
|
37
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IPlain"},
|
|
38
|
+
expected: text("Current Type: {@link IPlain}"),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "described",
|
|
42
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember"},
|
|
43
|
+
own: text("Member.\nSecond line."),
|
|
44
|
+
expected: text("Member.\nSecond line." + separator +
|
|
45
|
+
"Description of the current {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "described child of a described parent",
|
|
49
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.ICreate"},
|
|
50
|
+
own: text("Creation."),
|
|
51
|
+
expected: text("Creation." + separator +
|
|
52
|
+
"Description of the current {@link IMember.ICreate} type:\n\n> Creation." + separator +
|
|
53
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "undescribed child of a described parent",
|
|
57
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.IBare"},
|
|
58
|
+
expected: text("Current Type: {@link IMember.IBare}" + separator +
|
|
59
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "undescribed middle parent is skipped",
|
|
63
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.Deep.ILeaf"},
|
|
64
|
+
expected: text("Current Type: {@link IMember.Deep.ILeaf}" + separator +
|
|
65
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "empty own description renders quoted",
|
|
69
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.IEmpty"},
|
|
70
|
+
own: text(""),
|
|
71
|
+
expected: text("Description of the current {@link IMember.IEmpty} type:\n\n> " + separator +
|
|
72
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "parent missing from components",
|
|
76
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/INamespaceless.IItem"},
|
|
77
|
+
expected: text("Current Type: {@link INamespaceless.IItem}"),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "inline object keeps its own description",
|
|
81
|
+
schema: nativeiterate.JsonSchema{"type": "object"},
|
|
82
|
+
own: text("Inline."),
|
|
83
|
+
expected: text("Inline."),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "inline object without description",
|
|
87
|
+
schema: nativeiterate.JsonSchema{"type": "object"},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "foreign reference keeps its own description",
|
|
91
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/$defs/IPlain"},
|
|
92
|
+
own: text("Own."),
|
|
93
|
+
expected: text("Own."),
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
for _, c := range cases {
|
|
97
|
+
actual := llmReferenceDescription_root(c.schema, components, c.own)
|
|
98
|
+
if (actual == nil) != (c.expected == nil) || (actual != nil && *actual != *c.expected) {
|
|
99
|
+
t.Errorf("%s:\nexpected %s\nactual %s", c.name, llmReferenceDescriptionTest_show(c.expected), llmReferenceDescriptionTest_show(actual))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// TestLlmReferenceDescriptionAssign locks that the parameters object loses a
|
|
105
|
+
// stale description when the cascade yields none, and gains the cascade
|
|
106
|
+
// otherwise.
|
|
107
|
+
func TestLlmReferenceDescriptionAssign(t *testing.T) {
|
|
108
|
+
t.Parallel()
|
|
109
|
+
components := &nativeiterate.OpenApi_IComponents{
|
|
110
|
+
Schemas: map[string]nativeiterate.JsonSchema{"IPlain": {"type": "object"}},
|
|
111
|
+
}
|
|
112
|
+
output := map[string]any{"type": "object"}
|
|
113
|
+
llmReferenceDescription_assign(output, nativeiterate.JsonSchema{"$ref": "#/components/schemas/IPlain"}, components)
|
|
114
|
+
if output["description"] != "Current Type: {@link IPlain}" {
|
|
115
|
+
t.Fatalf("reference root: got %#v", output["description"])
|
|
116
|
+
}
|
|
117
|
+
inline := map[string]any{"type": "object", "description": ""}
|
|
118
|
+
llmReferenceDescription_assign(inline, nativeiterate.JsonSchema{"type": "object"}, components)
|
|
119
|
+
if value, ok := inline["description"]; ok == false || value != "" {
|
|
120
|
+
t.Fatalf("inline root must keep its own description: got %#v", inline)
|
|
121
|
+
}
|
|
122
|
+
bare := map[string]any{"type": "object"}
|
|
123
|
+
llmReferenceDescription_assign(bare, nativeiterate.JsonSchema{"type": "object"}, components)
|
|
124
|
+
if _, ok := bare["description"]; ok {
|
|
125
|
+
t.Fatalf("inline root without description must stay undescribed: got %#v", bare)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func llmReferenceDescriptionTest_show(value *string) string {
|
|
130
|
+
if value == nil {
|
|
131
|
+
return "<nil>"
|
|
132
|
+
}
|
|
133
|
+
return "\"" + *value + "\""
|
|
134
|
+
}
|
|
@@ -91,6 +91,15 @@ func MetadataDependency_active(checker *nativechecker.Checker) bool {
|
|
|
91
91
|
return metadataDependency_listener(checker).active()
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// MetadataDependency_touchFile reports a source file consulted outside the
|
|
95
|
+
// metadata iterator, so a complete transform dependency list still watches it.
|
|
96
|
+
func MetadataDependency_touchFile(checker *nativechecker.Checker, fileName string) {
|
|
97
|
+
listener := metadataDependency_listener(checker)
|
|
98
|
+
if listener.File != nil {
|
|
99
|
+
listener.File(fileName)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
// MetadataDependency_touchType reports the declaration files of a consulted
|
|
95
104
|
// type: both the structural symbol (interface / class / enum / object literal)
|
|
96
105
|
// and the type-name symbol (a `type` alias), which differ for aliased types.
|
|
@@ -469,6 +469,7 @@ func callExpressionTransformer_createFunctors() map[string]map[string]callExpres
|
|
|
469
469
|
"structuredOutput": func() callExpressionTransformerTask {
|
|
470
470
|
return nativellmtransformers.LlmStructuredOutputTransformer.Transform
|
|
471
471
|
},
|
|
472
|
+
"evaluation": func() callExpressionTransformerTask { return nativellmtransformers.LlmEvaluationTransformer.Transform },
|
|
472
473
|
"parameters": func() callExpressionTransformerTask { return nativellmtransformers.LlmParametersTransformer.Transform },
|
|
473
474
|
"schema": func() callExpressionTransformerTask { return nativellmtransformers.LlmSchemaTransformer.Transform },
|
|
474
475
|
"parse": func() callExpressionTransformerTask { return nativellmtransformers.LlmParseTransformer.Transform },
|
|
@@ -33,6 +33,10 @@ var fileTransformer_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
|
33
33
|
var fileTransformer_jsDocParsingMode sync.Map
|
|
34
34
|
|
|
35
35
|
func (fileTransformerNamespace) Transform(environments FileTransformer_IEnvironments) func(transformer any) FileTransformer_Type {
|
|
36
|
+
shared := environments.Extras.Shared
|
|
37
|
+
if shared == nil {
|
|
38
|
+
shared = &sync.Map{}
|
|
39
|
+
}
|
|
36
40
|
return func(transformer any) FileTransformer_Type {
|
|
37
41
|
return func(file *shimast.SourceFile) *shimast.SourceFile {
|
|
38
42
|
if file == nil || file.IsDeclarationFile {
|
|
@@ -53,6 +57,7 @@ func (fileTransformerNamespace) Transform(environments FileTransformer_IEnvironm
|
|
|
53
57
|
Emit: environments.EmitContext,
|
|
54
58
|
Importer: importer,
|
|
55
59
|
Extras: environments.Extras,
|
|
60
|
+
Shared: shared,
|
|
56
61
|
}
|
|
57
62
|
_ = transformer
|
|
58
63
|
fileTransformer_checkJsDocParsingMode(context, file)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"strings"
|
|
6
|
+
"sync"
|
|
7
|
+
|
|
8
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
|
+
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
10
|
+
nativellmprogrammers "github.com/samchon/typia/packages/typia/native/core/programmers/llm"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
func llmEvaluation_cachedDeclarationProbabilityErrors(context nativecontext.ITypiaContext) []nativellmprogrammers.LlmEvaluationProgrammer_IError {
|
|
14
|
+
if context.Program == nil {
|
|
15
|
+
return nil
|
|
16
|
+
}
|
|
17
|
+
return llmEvaluation_onceDeclarationProbabilityErrors(context.Shared, func() []nativellmprogrammers.LlmEvaluationProgrammer_IError {
|
|
18
|
+
return llmEvaluation_declarationProbabilityErrors(context.Program.SourceFiles())
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func llmEvaluation_onceDeclarationProbabilityErrors(shared *sync.Map, analyze func() []nativellmprogrammers.LlmEvaluationProgrammer_IError) []nativellmprogrammers.LlmEvaluationProgrammer_IError {
|
|
23
|
+
if shared == nil {
|
|
24
|
+
return analyze()
|
|
25
|
+
}
|
|
26
|
+
cached, _ := shared.LoadOrStore("llm.evaluation.probability.declarations", sync.OnceValue(analyze))
|
|
27
|
+
return cached.(func() []nativellmprogrammers.LlmEvaluationProgrammer_IError)()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Primitive aliases lose their declaration identity when TypeScript resolves
|
|
31
|
+
// them (boolean aliases become the same boolean type). Consequently, no walk
|
|
32
|
+
// of the resolved evaluation type can reliably distinguish a used annotation
|
|
33
|
+
// from an unused one. Validate tag placement syntactically across a program
|
|
34
|
+
// that uses llm.evaluation: only properties and enum members can carry it.
|
|
35
|
+
func llmEvaluation_declarationProbabilityErrors(files []*shimast.SourceFile) []nativellmprogrammers.LlmEvaluationProgrammer_IError {
|
|
36
|
+
errors := []nativellmprogrammers.LlmEvaluationProgrammer_IError{}
|
|
37
|
+
for _, file := range files {
|
|
38
|
+
if file == nil || !strings.Contains(file.Text(), "@probability") {
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
var walk func(*shimast.Node)
|
|
42
|
+
walk = func(node *shimast.Node) {
|
|
43
|
+
if node == nil {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
allowed := node.Kind == shimast.KindPropertySignature ||
|
|
47
|
+
node.Kind == shimast.KindPropertyDeclaration ||
|
|
48
|
+
node.Kind == shimast.KindGetAccessor ||
|
|
49
|
+
node.Kind == shimast.KindSetAccessor ||
|
|
50
|
+
node.Kind == shimast.KindEnumMember
|
|
51
|
+
if !allowed {
|
|
52
|
+
for _, doc := range node.JSDoc(nil) {
|
|
53
|
+
parsed := doc.AsJSDoc()
|
|
54
|
+
if parsed == nil || parsed.Tags == nil {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
for _, tag := range parsed.Tags.Nodes {
|
|
58
|
+
if tag != nil && tag.TagName() != nil && tag.TagName().Text() == "probability" {
|
|
59
|
+
name := "<anonymous>"
|
|
60
|
+
if identifier := node.Name(); identifier != nil {
|
|
61
|
+
switch identifier.Kind {
|
|
62
|
+
case shimast.KindIdentifier, shimast.KindStringLiteral, shimast.KindNumericLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
63
|
+
name = identifier.Text()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
kind := "declaration"
|
|
67
|
+
switch node.Kind {
|
|
68
|
+
case shimast.KindTypeAliasDeclaration:
|
|
69
|
+
kind = "type alias"
|
|
70
|
+
case shimast.KindInterfaceDeclaration:
|
|
71
|
+
kind = "interface"
|
|
72
|
+
case shimast.KindClassDeclaration, shimast.KindClassExpression:
|
|
73
|
+
kind = "class"
|
|
74
|
+
case shimast.KindEnumDeclaration:
|
|
75
|
+
kind = "enum"
|
|
76
|
+
}
|
|
77
|
+
errors = append(errors, nativellmprogrammers.LlmEvaluationProgrammer_IError{
|
|
78
|
+
Accessor: "$input",
|
|
79
|
+
Message: fmt.Sprintf("LLM evaluation @probability on %s %s in %s is unsupported; put it on a decision property or enum member.", kind, name, file.FileName()),
|
|
80
|
+
})
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
87
|
+
walk(child)
|
|
88
|
+
return false
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
walk(file.AsNode())
|
|
92
|
+
}
|
|
93
|
+
return errors
|
|
94
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"sync"
|
|
5
|
+
"sync/atomic"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
nativellmprogrammers "github.com/samchon/typia/packages/typia/native/core/programmers/llm"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestLlmEvaluationDeclarationProbabilityCache verifies one placement scan
|
|
12
|
+
// runs for every transform invocation sharing a program cache.
|
|
13
|
+
//
|
|
14
|
+
// File callbacks can run concurrently and must receive the same diagnostic
|
|
15
|
+
// result without redoing the program-wide scan. A new program gets a new map.
|
|
16
|
+
//
|
|
17
|
+
// 1. Read the result concurrently through one shared map.
|
|
18
|
+
// 2. Require one calculation and identical diagnostics for every reader.
|
|
19
|
+
// 3. Require a fresh map and an uncached call to calculate independently.
|
|
20
|
+
func TestLlmEvaluationDeclarationProbabilityCache(t *testing.T) {
|
|
21
|
+
var calls atomic.Int32
|
|
22
|
+
analyze := func() []nativellmprogrammers.LlmEvaluationProgrammer_IError {
|
|
23
|
+
calls.Add(1)
|
|
24
|
+
return []nativellmprogrammers.LlmEvaluationProgrammer_IError{{
|
|
25
|
+
Accessor: "$input",
|
|
26
|
+
Message: "unsupported declaration",
|
|
27
|
+
}}
|
|
28
|
+
}
|
|
29
|
+
shared := &sync.Map{}
|
|
30
|
+
var group sync.WaitGroup
|
|
31
|
+
for range 16 {
|
|
32
|
+
group.Add(1)
|
|
33
|
+
go func() {
|
|
34
|
+
defer group.Done()
|
|
35
|
+
errors := llmEvaluation_onceDeclarationProbabilityErrors(shared, analyze)
|
|
36
|
+
if len(errors) != 1 || errors[0].Message != "unsupported declaration" {
|
|
37
|
+
t.Errorf("cached diagnostic changed: %+v", errors)
|
|
38
|
+
}
|
|
39
|
+
}()
|
|
40
|
+
}
|
|
41
|
+
group.Wait()
|
|
42
|
+
if count := calls.Load(); count != 1 {
|
|
43
|
+
t.Fatalf("one program must scan once, got %d scans", count)
|
|
44
|
+
}
|
|
45
|
+
llmEvaluation_onceDeclarationProbabilityErrors(&sync.Map{}, analyze)
|
|
46
|
+
llmEvaluation_onceDeclarationProbabilityErrors(nil, analyze)
|
|
47
|
+
if count := calls.Load(); count != 3 {
|
|
48
|
+
t.Fatalf("fresh and uncached programs must recalculate, got %d scans", count)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
|
+
nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
|
|
6
|
+
nativellmprogrammers "github.com/samchon/typia/packages/typia/native/core/programmers/llm"
|
|
7
|
+
schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
8
|
+
nativetransform "github.com/samchon/typia/packages/typia/native/transform/internal"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
type llmEvaluationTransformerNamespace struct{}
|
|
12
|
+
|
|
13
|
+
var LlmEvaluationTransformer = llmEvaluationTransformerNamespace{}
|
|
14
|
+
|
|
15
|
+
func (llmEvaluationTransformerNamespace) Transform(props nativetransform.ITransformProps) *shimast.Node {
|
|
16
|
+
top, typ, ok := llmTransformer_type_argument(props, "typia.llm.evaluation")
|
|
17
|
+
if ok == false {
|
|
18
|
+
return props.Expression.AsNode()
|
|
19
|
+
}
|
|
20
|
+
if typ != nil && typ.IsTypeParameter() {
|
|
21
|
+
panic(nativetransform.NewTransformerError(nativetransform.TransformerError_IProps{Code: "typia.llm.evaluation", Message: "non-specified generic argument."}))
|
|
22
|
+
}
|
|
23
|
+
metadata := llmTransformer_analyze(llmTransformer_analyzeProps{
|
|
24
|
+
Context: props.Context,
|
|
25
|
+
Type: typ,
|
|
26
|
+
Code: "typia.llm.evaluation",
|
|
27
|
+
Absorb: true,
|
|
28
|
+
StrictObjectMembers: true,
|
|
29
|
+
Validate: func(struct {
|
|
30
|
+
Metadata *schemametadata.MetadataSchema
|
|
31
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
32
|
+
Top *schemametadata.MetadataSchema
|
|
33
|
+
}) []string {
|
|
34
|
+
return nil
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
plan, errors := nativellmprogrammers.LlmEvaluationProgrammer.Compose(metadata)
|
|
38
|
+
errors = append(errors, llmEvaluation_cachedDeclarationProbabilityErrors(props.Context)...)
|
|
39
|
+
// The placement scan depends on every program source, including files with
|
|
40
|
+
// no tag today: adding one later must invalidate a cached successful emit.
|
|
41
|
+
if props.Context.Program != nil && schemametadata.MetadataDependency_active(props.Context.Checker) {
|
|
42
|
+
for _, file := range props.Context.Program.SourceFiles() {
|
|
43
|
+
if file != nil {
|
|
44
|
+
schemametadata.MetadataDependency_touchFile(props.Context.Checker, file.FileName())
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if len(errors) != 0 {
|
|
49
|
+
panic(nativetransform.NewTransformerError(nativetransform.TransformerError_IProps{
|
|
50
|
+
Code: "typia.llm.evaluation",
|
|
51
|
+
Message: nativellmprogrammers.LlmEvaluationProgrammer.Message(errors),
|
|
52
|
+
}))
|
|
53
|
+
}
|
|
54
|
+
return nativellmprogrammers.LlmEvaluationProgrammer.Write(nativellmprogrammers.LlmEvaluationProgrammer_IWriteProps{
|
|
55
|
+
Context: props.Context,
|
|
56
|
+
Metadata: metadata,
|
|
57
|
+
Name: llmTransformer_type_name(top),
|
|
58
|
+
}, plan)
|
|
59
|
+
}
|
|
@@ -68,12 +68,13 @@ func (llmSchemaTransformerNamespace) Transform(props nativetransform.ITransformP
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
type llmTransformer_analyzeProps struct {
|
|
71
|
-
Context
|
|
72
|
-
Type
|
|
73
|
-
Code
|
|
74
|
-
Absorb
|
|
75
|
-
Functional
|
|
76
|
-
|
|
71
|
+
Context nativecontext.ITypiaContext
|
|
72
|
+
Type *shimchecker.Type
|
|
73
|
+
Code string
|
|
74
|
+
Absorb bool
|
|
75
|
+
Functional bool
|
|
76
|
+
StrictObjectMembers bool
|
|
77
|
+
Validate func(props struct {
|
|
77
78
|
Metadata *schemametadata.MetadataSchema
|
|
78
79
|
Explore nativefactories.MetadataFactory_IExplore
|
|
79
80
|
Top *schemametadata.MetadataSchema
|
|
@@ -105,11 +106,13 @@ func llmTransformer_analyze(props llmTransformer_analyzeProps) *schemametadata.M
|
|
|
105
106
|
result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
106
107
|
Checker: props.Context.Checker,
|
|
107
108
|
Options: nativefactories.MetadataFactory_IOptions{
|
|
108
|
-
Absorb:
|
|
109
|
-
Escape:
|
|
110
|
-
Constant:
|
|
111
|
-
Functional:
|
|
112
|
-
|
|
109
|
+
Absorb: props.Absorb,
|
|
110
|
+
Escape: true,
|
|
111
|
+
Constant: true,
|
|
112
|
+
Functional: props.Functional,
|
|
113
|
+
Methods: props.StrictObjectMembers,
|
|
114
|
+
StrictObjectMembers: props.StrictObjectMembers,
|
|
115
|
+
Validate: props.Validate,
|
|
113
116
|
},
|
|
114
117
|
Components: schemametadata.NewMetadataCollection(&schemametadata.MetadataCollection_IOptions{
|
|
115
118
|
Replace: schemametadata.MetadataCollection_replace,
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
package llm
|
|
5
5
|
|
|
6
6
|
import (
|
|
7
|
-
|
|
7
|
+
"testing"
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
nativetransform "github.com/samchon/typia/packages/typia/native/transform/internal"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
// TestLLMTransformerEntryCoverage exercises LLM transform entrypoints.
|
|
@@ -16,33 +16,35 @@ import (
|
|
|
16
16
|
// guards. Empty transform props visit those entrypoints and recover their guard
|
|
17
17
|
// failures deterministically.
|
|
18
18
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// 1. Visit schema, parameters, application, structured-output, and evaluation
|
|
20
|
+
// wrappers.
|
|
21
|
+
// 2. Visit controller and parse/coerce scalar wrappers.
|
|
22
|
+
// 3. Visit create-parse and create-coerce factory wrappers.
|
|
23
|
+
// 4. Recover expected guard panics for every wrapper call.
|
|
23
24
|
func TestLLMTransformerEntryCoverage(t *testing.T) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
props := nativetransform.ITransformProps{}
|
|
26
|
+
for _, run := range []func(){
|
|
27
|
+
func() { LlmSchemaTransformer.Transform(props) },
|
|
28
|
+
func() { LlmParametersTransformer.Transform(props) },
|
|
29
|
+
func() { LlmApplicationTransformer.Transform(props) },
|
|
30
|
+
func() { LlmStructuredOutputTransformer.Transform(props) },
|
|
31
|
+
func() { LlmEvaluationTransformer.Transform(props) },
|
|
32
|
+
func() { LlmControllerTransformer.Transform(props) },
|
|
33
|
+
func() { LlmParseTransformer.Transform(props) },
|
|
34
|
+
func() { LlmCoerceTransformer.Transform(props) },
|
|
35
|
+
func() { LlmCreateParseTransformer.Transform(props) },
|
|
36
|
+
func() { LlmCreateCoerceTransformer.Transform(props) },
|
|
37
|
+
} {
|
|
38
|
+
expectLLMPanic(t, run)
|
|
39
|
+
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
func expectLLMPanic(t *testing.T, run func()) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
t.Helper()
|
|
44
|
+
defer func() {
|
|
45
|
+
if recover() == nil {
|
|
46
|
+
t.Fatal("expected LLM transformer panic")
|
|
47
|
+
}
|
|
48
|
+
}()
|
|
49
|
+
run()
|
|
48
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typia",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.0.0",
|
|
4
4
|
"description": "Superfast runtime validators with only one line",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"homepage": "https://typia.io",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"randexp": "^0.5.3",
|
|
46
|
-
"@typia/
|
|
47
|
-
"@typia/
|
|
46
|
+
"@typia/utils": "^15.0.0",
|
|
47
|
+
"@typia/interface": "^15.0.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"ttsc": ">=0.19.2"
|