Json Schema for LLMs
This commit is contained in:
+109
-116
@@ -14,23 +14,17 @@ uses
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Attributes,
|
||||
Myc.Ast.RTL,
|
||||
Myc.Ast.RTL.Core,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast.Script,
|
||||
Myc.Ast.Json;
|
||||
Myc.Ast.Json,
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
{ Scans AST, RTL and Scope metadata to generate a context-aware LLM system prompt. }
|
||||
TAstSchema = class
|
||||
strict private
|
||||
type
|
||||
TRtlMeta = record
|
||||
Doc: string;
|
||||
Signatures: TArray<string>;
|
||||
end;
|
||||
|
||||
TFieldInfo = record
|
||||
Idx: Integer;
|
||||
Name: string;
|
||||
@@ -39,18 +33,17 @@ type
|
||||
|
||||
class function GetTsType(Kind: TFieldKind): string; static;
|
||||
class function TypeToTs(const AType: IStaticType): string; static;
|
||||
class function GetRtlMetadata: TDictionary<string, TRtlMeta>; static;
|
||||
public
|
||||
// Generates the TypeScript definition for AST nodes
|
||||
// Generates the TypeScript definition for AST nodes using RTTI on the Node Interfaces
|
||||
class function GenerateTypeScriptDefinition: string; static;
|
||||
|
||||
// Unified section: Lists only symbols available in the current scope with their docs/types
|
||||
// Unified section: Lists symbols available in the given scope with their docs and types.
|
||||
class function GenerateAvailableSymbols(const AScope: IExecutionScope): string; static;
|
||||
|
||||
// Documentation for all RTL functions (used as fallback or for general prompts)
|
||||
// Generates documentation for the standard library by creating a temporary scope.
|
||||
class function GenerateAllRtlDocumentation: string; static;
|
||||
|
||||
// Detailed generation rules
|
||||
// Detailed generation rules for the LLM
|
||||
class function GenerateGenerationRules: string; static;
|
||||
|
||||
// The final consolidated system prompt for the AI
|
||||
@@ -81,37 +74,106 @@ begin
|
||||
exit('any');
|
||||
|
||||
case AType.Kind of
|
||||
stUnknown: Result := 'any';
|
||||
stVoid: Result := 'void';
|
||||
stOrdinal, stFloat: Result := 'number';
|
||||
stBoolean: Result := 'boolean';
|
||||
stDateTime: Result := 'datetime';
|
||||
stText: Result := 'string';
|
||||
stKeyword: Result := 'keyword';
|
||||
|
||||
stSeries: Result := Format('Series<%s>', [TypeToTs(AType.AsSeries.ElementType)]);
|
||||
|
||||
stRecord:
|
||||
begin
|
||||
var def := AType.AsRecord.Definition;
|
||||
var fields := TList<string>.Create;
|
||||
try
|
||||
for var i := 0 to def.Count - 1 do
|
||||
fields.Add(Format('%s: %s', [def.Keywords[i].Name, def[i].ToString.ToLower]));
|
||||
begin
|
||||
var k := def.Keywords[i].Name;
|
||||
var tStr: string;
|
||||
case def[i] of
|
||||
TScalar.TKind.Ordinal, TScalar.TKind.Float: tStr := 'number';
|
||||
TScalar.TKind.Boolean: tStr := 'boolean';
|
||||
TScalar.TKind.DateTime: tStr := 'datetime';
|
||||
TScalar.TKind.Keyword: tStr := 'keyword';
|
||||
else
|
||||
tStr := 'any';
|
||||
end;
|
||||
fields.Add(Format('%s: %s', [k, tStr]));
|
||||
end;
|
||||
Result := '{ ' + string.Join(', ', fields.ToArray) + ' }';
|
||||
finally
|
||||
fields.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
stGenericRecord:
|
||||
begin
|
||||
var def := AType.AsGenericRecord.GenericDefinition;
|
||||
var fields := TList<string>.Create;
|
||||
try
|
||||
for var i := 0 to def.Count - 1 do
|
||||
fields.Add(Format('%s: %s', [def.Keywords[i].Name, TypeToTs(def[i])]));
|
||||
Result := '{ ' + string.Join(', ', fields.ToArray) + ' }';
|
||||
finally
|
||||
fields.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
stRecordSeries: Result := 'Stream<' + TypeToTs(TTypes.CreateRecord(AType.AsRecord.Definition)) + '>';
|
||||
|
||||
stTuple:
|
||||
begin
|
||||
var elems := AType.AsTuple.Elements;
|
||||
var elemStrings := TList<string>.Create;
|
||||
try
|
||||
for var t in elems do
|
||||
elemStrings.Add(TypeToTs(t));
|
||||
Result := '[' + string.Join(', ', elemStrings.ToArray) + ']';
|
||||
finally
|
||||
elemStrings.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
stVector: Result := TypeToTs(AType.AsVector.ElementType) + '[]';
|
||||
|
||||
stMatrix:
|
||||
begin
|
||||
var dims := AType.AsMatrix.Dimensions;
|
||||
Result := TypeToTs(AType.AsMatrix.ElementType);
|
||||
for var k := 0 to High(dims) do
|
||||
Result := Result + '[]';
|
||||
end;
|
||||
|
||||
stMethod:
|
||||
begin
|
||||
if Length(AType.AsMethod.Signatures) = 0 then
|
||||
var signatures := AType.AsMethod.Signatures;
|
||||
if Length(signatures) = 0 then
|
||||
exit('function');
|
||||
var sig := AType.AsMethod.Signatures[0];
|
||||
var args := TList<string>.Create;
|
||||
|
||||
var sigStrings := TList<string>.Create;
|
||||
try
|
||||
for var p in sig.ParamTypes do
|
||||
args.Add(TypeToTs(p));
|
||||
Result := Format('(%s) => %s', [string.Join(', ', args.ToArray), TypeToTs(sig.ReturnType)]);
|
||||
for var sig in signatures do
|
||||
begin
|
||||
var args := TList<string>.Create;
|
||||
try
|
||||
for var p in sig.ParamTypes do
|
||||
args.Add(TypeToTs(p));
|
||||
|
||||
var s := Format('(%s) => %s', [string.Join(', ', args.ToArray), TypeToTs(sig.ReturnType)]);
|
||||
|
||||
// Deduplicate: If multiple RTL overloads map to the same TS signature, only show it once.
|
||||
if not sigStrings.Contains(s) then
|
||||
sigStrings.Add(s);
|
||||
finally
|
||||
args.Free;
|
||||
end;
|
||||
end;
|
||||
Result := string.Join(' | ', sigStrings.ToArray);
|
||||
finally
|
||||
args.Free;
|
||||
sigStrings.Free;
|
||||
end;
|
||||
end;
|
||||
else
|
||||
@@ -122,56 +184,6 @@ begin
|
||||
Result := Result + ' | null';
|
||||
end;
|
||||
|
||||
class function TAstSchema.GetRtlMetadata: TDictionary<string, TRtlMeta>;
|
||||
var
|
||||
ctx: TRttiContext;
|
||||
typ: TRttiType;
|
||||
meth: TRttiMethod;
|
||||
attr: TCustomAttribute;
|
||||
meta: TRtlMeta;
|
||||
rtlName: string;
|
||||
sigs: TList<string>;
|
||||
begin
|
||||
Result := TDictionary<string, TRtlMeta>.Create;
|
||||
sigs := TList<string>.Create;
|
||||
try
|
||||
typ := ctx.GetType(TypeInfo(TRtlFunctions));
|
||||
for meth in typ.GetMethods do
|
||||
begin
|
||||
rtlName := '';
|
||||
meta.Doc := '';
|
||||
sigs.Clear;
|
||||
for attr in meth.GetAttributes do
|
||||
begin
|
||||
if attr is AstDocAttribute then
|
||||
meta.Doc := AstDocAttribute(attr).Description;
|
||||
if attr is TRtlExportAttribute then
|
||||
rtlName := TRtlExportAttribute(attr).Name;
|
||||
if attr is AstSignatureAttribute then
|
||||
sigs.Add(AstSignatureAttribute(attr).Signature);
|
||||
end;
|
||||
if rtlName <> '' then
|
||||
begin
|
||||
meta.Signatures := sigs.ToArray;
|
||||
if Result.ContainsKey(rtlName) then
|
||||
begin
|
||||
var existing := Result[rtlName];
|
||||
var combinedSigs := TList<string>.Create;
|
||||
combinedSigs.AddRange(existing.Signatures);
|
||||
combinedSigs.AddRange(meta.Signatures);
|
||||
existing.Signatures := combinedSigs.ToArray;
|
||||
combinedSigs.Free;
|
||||
Result[rtlName] := existing;
|
||||
end
|
||||
else
|
||||
Result.Add(rtlName, meta);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
sigs.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TAstSchema.GenerateTypeScriptDefinition: string;
|
||||
var
|
||||
ctx: TRttiContext;
|
||||
@@ -215,6 +227,7 @@ begin
|
||||
if exampleList = nil then
|
||||
exampleList := TList<string>.Create;
|
||||
try
|
||||
// Parse example script to ensure valid JSON output in documentation
|
||||
var node := TAstScript.Parse(AstScriptExampleAttribute(attr).Script);
|
||||
var jsonVal := jsonConv.Serialize(node);
|
||||
try
|
||||
@@ -224,7 +237,7 @@ begin
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
exampleList.Add('/* Error in example */');
|
||||
exampleList.Add('/* Error in example: ' + E.Message + ' */');
|
||||
end;
|
||||
end;
|
||||
if attr is AstFieldAttribute then
|
||||
@@ -309,87 +322,67 @@ end;
|
||||
class function TAstSchema.GenerateAvailableSymbols(const AScope: IExecutionScope): string;
|
||||
var
|
||||
sb: TStringBuilder;
|
||||
rtlMeta: TDictionary<string, TRtlMeta>;
|
||||
layout: IScopeLayout;
|
||||
descriptor: IScopeDescriptor;
|
||||
symbols: TArray<string>;
|
||||
name: string;
|
||||
meta: TRtlMeta;
|
||||
name, doc: string;
|
||||
typ: IStaticType;
|
||||
slot: Integer;
|
||||
begin
|
||||
if not Assigned(AScope) then
|
||||
exit('');
|
||||
|
||||
rtlMeta := GetRtlMetadata;
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
layout := AScope.Descriptor.Layout;
|
||||
descriptor := AScope.Descriptor;
|
||||
layout := descriptor.Layout;
|
||||
symbols := layout.GetSymbols;
|
||||
TArray.Sort<string>(symbols);
|
||||
|
||||
sb.AppendLine('### 3. Available Symbols (Environment & Library)');
|
||||
sb.AppendLine('### Available Symbols (Environment & Library)');
|
||||
sb.AppendLine('The following symbols are currently defined and can be used in your script:');
|
||||
sb.AppendLine;
|
||||
|
||||
for name in symbols do
|
||||
begin
|
||||
// Filter internal compiler symbols
|
||||
if name.StartsWith('<') or name.EndsWith('>') then
|
||||
continue;
|
||||
|
||||
slot := layout.FindSlot(name);
|
||||
typ := descriptor.GetSymbolType(slot);
|
||||
doc := layout.GetSymbolDoc(name);
|
||||
|
||||
sb.Append(Format('- `%s`', [name]));
|
||||
|
||||
if rtlMeta.TryGetValue(name, meta) then
|
||||
begin
|
||||
if Length(meta.Signatures) > 0 then
|
||||
sb.Append(': ' + string.Join(' | ', meta.Signatures));
|
||||
if meta.Doc <> '' then
|
||||
sb.Append(' — ' + meta.Doc);
|
||||
end
|
||||
else
|
||||
begin
|
||||
typ := AScope.Descriptor.GetSymbolType(layout.FindSlot(name));
|
||||
if (typ <> nil) then
|
||||
sb.Append(': ' + TypeToTs(typ));
|
||||
end;
|
||||
|
||||
if doc <> '' then
|
||||
sb.Append(' — ' + doc);
|
||||
|
||||
sb.AppendLine;
|
||||
end;
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
rtlMeta.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TAstSchema.GenerateAllRtlDocumentation: string;
|
||||
var
|
||||
rtlMeta: TDictionary<string, TRtlMeta>;
|
||||
sb: TStringBuilder;
|
||||
pair: TPair<string, TRtlMeta>;
|
||||
begin
|
||||
rtlMeta := GetRtlMetadata;
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.AppendLine('### 3. Runtime Library (Global Functions)');
|
||||
sb.AppendLine;
|
||||
for pair in rtlMeta do
|
||||
begin
|
||||
sb.Append(Format('- `%s`', [pair.Key]));
|
||||
if Length(pair.Value.Signatures) > 0 then
|
||||
sb.Append(': ' + string.Join(' | ', pair.Value.Signatures));
|
||||
if pair.Value.Doc <> '' then
|
||||
sb.Append(' — ' + pair.Value.Doc);
|
||||
sb.AppendLine;
|
||||
end;
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
rtlMeta.Free;
|
||||
end;
|
||||
// Create a temporary root scope.
|
||||
// The True parameter triggers the registration of all standard libraries (RTL).
|
||||
// This populates the scope with function definitions and their documentation strings.
|
||||
var tempScope := TAst.CreateScope(nil, nil, True);
|
||||
Result := GenerateAvailableSymbols(tempScope);
|
||||
end;
|
||||
|
||||
class function TAstSchema.GenerateGenerationRules: string;
|
||||
begin
|
||||
Result :=
|
||||
'''
|
||||
**⚠️ CRITICAL GENERATION RULES:**
|
||||
**CRITICAL GENERATION RULES:**
|
||||
|
||||
1. **NO JSON OBJECTS:** Do not use `{}`. Every node MUST be a JSON Array `["Tag", Arg1, Arg2]`.
|
||||
2. **EXPLICIT TUPLES:** Fields defined as `Tuple` MUST be wrapped in `["Tuple", [...]]`.
|
||||
@@ -412,12 +405,12 @@ begin
|
||||
You are a compiler frontend for a domain-specific scripting language.
|
||||
Your task is to generate a valid Abstract Syntax Tree (AST) in a specific Compact JSON format.
|
||||
|
||||
### 1. AST Schema (TypeScript Definition)
|
||||
### AST Schema (TypeScript Definition)
|
||||
'''
|
||||
+ sLineBreak
|
||||
+ GenerateTypeScriptDefinition
|
||||
+ sLineBreak
|
||||
+ '### 2. Constraints & Rules'
|
||||
+ '### Constraints & Rules'
|
||||
+ sLineBreak
|
||||
+ GenerateGenerationRules
|
||||
+ sLineBreak;
|
||||
|
||||
Reference in New Issue
Block a user