Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Sources/LLMMacrosImplementation/GeneratableMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ public struct GeneratableMacro: MemberMacro, ExtensionMacro {
let schemaExpr = schema(for: unwrappedType, in: declaration, context: context)
return (name, schemaExpr, !isOptional)
}
let emptyStringToken="\"\""
let propertyExprsString = props.map { "\"\\\"" + $0.name + "\\\": \" + " + $0.schema }.joined(separator: " + \",\" + ")
let propertyExprsStringFinal = propertyExprsString.isEmpty ? emptyStringToken : propertyExprsString
let requiredExprString = props.filter { $0.isRequired }.map { "\"\\\"" + $0.name + "\\\"\"" }.joined(separator: " + \",\" + ")
let requiredExprStringFinal = requiredExprString.isEmpty ? emptyStringToken : requiredExprString
return [
"""
public static var jsonSchema: String {
return "{ \\"type\\": \\"object\\", \\"properties\\": {" + \(raw: propertyExprsString) + "}, \\"required\\": [" + \(raw: requiredExprString) + "] }"
return "{ \\"type\\": \\"object\\", \\"properties\\": {" + \(raw: propertyExprsStringFinal) + "}, \\"required\\": [" + \(raw: requiredExprStringFinal) + "] }"
}
"""
]
Expand Down Expand Up @@ -149,4 +152,4 @@ enum MacroError: Error, CustomStringConvertible {
case .notAStruct: "Can only be applied to a struct."
}
}
}
}
52 changes: 52 additions & 0 deletions Tests/LLMTests/LLMTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -779,4 +779,56 @@ final class LLMTests {
#expect(assignee["name"] is String)
#expect(assignee["age"] is Int)
}

@Generatable
struct EmptyTestStruct { }

@Test
func testEmptyGeneratable() async throws {
let bot = try await LLM(from: model)!

let result = try await bot.respond(
to: "Create the void",
as: EmptyTestStruct.self
)
let project = result.value
let output = result.rawOutput

print("Project: \(project)")
print("Raw output: \(output)")

let jsonData = output.data(using: String.Encoding.utf8)!
let parsed = try JSONSerialization.jsonObject(with: jsonData) as! [String: Any]
#expect(parsed.isEmpty)
}

@Generatable
struct AllNullableAttributes {
let first: Int?
let second: Int?
}

@Test
func testStructWithNullableAttributes() async throws {
let bot = try await LLM(from: model)!

print(AllNullableAttributes.jsonSchema)

// Note: it seems it is hard to tell this LLM to return an empty JSON or drop an attribute,
// so stick to just simple type checking
let result = try await bot.respond(
to: "Return something",
as: AllNullableAttributes.self
)
let project = result.value
let output = result.rawOutput

print("Project: \(project)")
print("Raw output: \(output)")

let jsonData = output.data(using: String.Encoding.utf8)!
let parsed = try JSONSerialization.jsonObject(with: jsonData) as! [String: Any]
#expect(parsed["first"] is Int?)
#expect(parsed["second"] is Int?)
}
}