Gemini Test

This commit is contained in:
Michael Schimmel
2026-01-08 14:13:41 +01:00
parent a25fec38f8
commit a62ba89bca
8 changed files with 2879 additions and 44 deletions
+94 -44
View File
@@ -47,20 +47,19 @@ class function TAstSchema.GetJsonType(Kind: TFieldKind): TJSONObject;
Result.AddPair('type', ATypeName);
end;
function JConst(const AVal: string): TJSONObject;
// Use enum for const strings as it's widely supported
function JEnum(const AVal: string): TJSONObject;
var
arr: TJSONArray;
begin
Result := TJSONObject.Create;
Result.AddPair('const', AVal);
Result := JType('string');
arr := TJSONArray.Create;
arr.Add(AVal);
Result.AddPair('enum', arr);
end;
function JArray(AItems: TJSONValue): TJSONObject;
begin
Result := JType('array');
Result.AddPair('items', AItems);
end;
// Combined function: Creates array from items and sets prefixItems + limits
function JFixedArray(const AItems: array of TJSONValue): TJSONObject;
// Tuple Array (Fixed items)
function JTupleArray(const AItems: array of TJSONValue): TJSONObject;
var
arr: TJSONArray;
item: TJSONValue;
@@ -68,19 +67,22 @@ class function TAstSchema.GetJsonType(Kind: TFieldKind): TJSONObject;
begin
Result := JType('array');
// "items" as an array defines a Tuple schema in older drafts (supported by Gemini)
arr := TJSONArray.Create;
for item in AItems do
arr.AddElement(item);
count := arr.Count;
Result.AddPair('prefixItems', arr);
Result.AddPair('items', arr); // Items as Array = Tuple Definition
Result.AddPair('minItems', TJSONNumber.Create(count));
Result.AddPair('maxItems', TJSONNumber.Create(count));
Result.AddPair('additionalItems', TJSONBool.Create(False)); // No extra items allowed
end;
function JTuple(AItems: TJSONValue): TJSONObject;
begin
Result := JFixedArray([JConst('Tuple'), AItems]);
// ["Tuple", [...]]
Result := JTupleArray([JEnum('Tuple'), AItems]);
end;
function JAnyOf(AOptions: array of TJSONValue): TJSONObject;
@@ -97,7 +99,7 @@ class function TAstSchema.GetJsonType(Kind: TFieldKind): TJSONObject;
{$endregion}
var
pair, selItems, selTuple, entryContent, entryNode: TJSONObject;
pair, selTuple, entryNode: TJSONObject;
begin
case Kind of
fkNode: Result := JDefRef('Node');
@@ -105,28 +107,54 @@ begin
fkKeyword: Result := JDefRef('Key');
fkLambda: Result := JDefRef('Fn');
fkIdentifier: Result := JDefRef('Id');
fkString: Result := JType('string');
fkValue: Result := JAnyOf([JType('number'), JType('string'), JType('boolean')]);
fkNullableNode: Result := JAnyOf([JDefRef('Node'), JType('null')]);
fkArrayOfNodes: Result := JArray(JDefRef('Node'));
fkValue:
begin
// number | string | boolean
Result := JAnyOf([JType('number'), JType('string'), JType('boolean')]);
end;
fkNullableNode:
begin
// Node | null
// Note: OpenAPI/Gemini often prefers 'nullable: true' over explicit null type,
// but anyOf [Node, {type: null}] works in full JSON schema.
Result := JAnyOf([JDefRef('Node'), JType('null')]);
end;
fkArrayOfNodes:
begin
// standard array of Nodes
Result := JType('array');
Result.AddPair('items', JDefRef('Node'));
end;
fkArrayOfPairs:
begin
// [[Cond, Branch], ...]
pair := JFixedArray([JDefRef('Node'), JDefRef('Node')]);
Result := JArray(pair);
pair := JTupleArray([JDefRef('Node'), JDefRef('Node')]);
Result := JType('array');
Result.AddPair('items', pair);
end;
fkPipeInputs:
begin
// 1. Selector Tuple: ["Tuple", [Key, ...]]
selTuple := JTuple(JArray(JDefRef('Key')));
// Note: This is an array of Keys inside the Tuple structure
var keyArray := JType('array');
keyArray.AddPair('items', JDefRef('Key'));
selTuple := JTuple(keyArray);
// 2. Entry Node: ["Tuple", [Id, SelectorTuple]]
entryNode := JTuple(JFixedArray([JDefRef('Id'), selTuple]));
entryNode := JTupleArray([JDefRef('Id'), selTuple]);
// 3. Outer Inputs List: ["Tuple", [EntryNode, ...]]
Result := JTuple(JArray(entryNode));
// 3. Wrap in ["Tuple", [Array of EntryNodes]]
var entriesArray := JType('array');
entriesArray.AddPair('items', entryNode);
Result := JTuple(entriesArray);
end;
else
Result := TJSONObject.Create;
@@ -142,7 +170,11 @@ var
anyOfNodes: TJSONArray;
tag: string;
fields: TList<AstFieldAttribute>;
minCount, maxCount: Integer;
itemsArr: TJSONArray;
// Properties Wrapper
props, programProp: TJSONObject;
reqArr: TJSONArray;
begin
ctx := TRttiContext.Create;
Result := TJSONObject.Create;
@@ -150,7 +182,7 @@ begin
anyOfNodes := TJSONArray.Create;
try
// 1. Scan all interfaces for AstTag metadata
// 1. Scan all interfaces
for typ in ctx.GetTypes do
begin
if typ.TypeKind <> tkInterface then
@@ -170,39 +202,41 @@ begin
if tag = '' then
continue;
// Sort fields by index
// Sort fields
fields.Sort(
TComparer<AstFieldAttribute>
.Construct(function(const L, R: AstFieldAttribute): Integer begin Result := L.Index - R.Index; end)
);
// Construct Tuple Definition for this Tag: ["Tag", Arg1, Arg2]
nodeDef := TJSONObject.Create;
nodeDef.AddPair('type', 'array');
var items := TJSONArray.Create;
items.AddElement(TJSONObject.Create.AddPair('const', tag));
itemsArr := TJSONArray.Create;
minCount := 1;
maxCount := 1;
// Item 0: The Tag (as Const/Enum)
var tagConst := TJSONObject.Create;
tagConst.AddPair('type', 'string');
var enumArr := TJSONArray.Create;
enumArr.Add(tag);
tagConst.AddPair('enum', enumArr);
itemsArr.AddElement(tagConst);
// Items 1..N: The Fields
for var f in fields do
begin
inc(maxCount);
// Only non-nullable fields increase minCount
if f.Kind <> fkNullableNode then
minCount := maxCount;
items.AddElement(GetJsonType(f.Kind));
itemsArr.AddElement(GetJsonType(f.Kind));
end;
nodeDef.AddPair('prefixItems', items);
nodeDef.AddPair('minItems', TJSONNumber.Create(minCount));
nodeDef.AddPair('maxItems', TJSONNumber.Create(maxCount));
// additionalItems removed as maxItems guarantees strictness
nodeDef.AddPair('items', itemsArr);
nodeDef.AddPair('minItems', TJSONNumber.Create(itemsArr.Count));
nodeDef.AddPair('maxItems', TJSONNumber.Create(itemsArr.Count));
nodeDef.AddPair('additionalItems', TJSONBool.Create(False));
// Add to defs
defs.AddPair(tag, nodeDef);
// Add reference to the global AstNode union
// Add to main Node Union
astNodeRef := TJSONObject.Create;
astNodeRef.AddPair('$ref', '#/$defs/' + tag);
anyOfNodes.AddElement(astNodeRef);
@@ -212,13 +246,29 @@ begin
end;
end;
// 2. Define the base AstNode anyOf union
// 2. Define generic "Node" union
var astNodeBase := TJSONObject.Create;
// WICHTIG: Das fehlende "type": "array" hat den Fehler verursacht.
astNodeBase.AddPair('type', 'array');
astNodeBase.AddPair('anyOf', anyOfNodes);
defs.AddPair('Node', astNodeBase);
// 3. Assemble final Root Object
Result.AddPair('$ref', '#/$defs/Node');
// 3. ROOT OBJECT WRAPPER
Result.AddPair('type', 'object');
props := TJSONObject.Create;
programProp := TJSONObject.Create;
programProp.AddPair('$ref', '#/$defs/Node');
props.AddPair('program', programProp);
Result.AddPair('properties', props);
reqArr := TJSONArray.Create;
reqArr.Add('program');
Result.AddPair('required', reqArr);
Result.AddPair('additionalProperties', TJSONBool.Create(False));
Result.AddPair('$defs', defs);
finally