Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ded134c
Fix SystemLanguageModel to pass schema for structured generation
eastriverlee Dec 20, 2025
0fa246d
Implement logit-constrained structured generation for LlamaLanguageModel
eastriverlee Dec 20, 2025
be79024
Implement logit-constrained structured generation for MLXLanguageModel
eastriverlee Dec 20, 2025
887964a
Fix duplicate type crash in schema generation
eastriverlee Dec 20, 2025
ecae1bd
Enforce count + numeric range guides
eastriverlee Dec 21, 2025
9fb930b
Respect temperature for structured generation
eastriverlee Dec 21, 2025
77950e3
Refactor Llama and MLX structured generation to shared constrained ge…
eastriverlee Dec 21, 2025
c8fdb9d
swift format -i -r .
mattt Jan 20, 2026
200886b
Restore SystemLanguageModel.swift from HEAD of main
mattt Jan 20, 2026
51294c6
Align structured generation prompts and defaults, and enrich schema p…
mattt Jan 20, 2026
2488201
Respect schema prompt flag and enhance structured prompts with JSONSc…
mattt Jan 20, 2026
04650e1
Add documentation comments to helper methods
mattt Jan 20, 2026
a5ccc1e
Refactor tests and fixtures
mattt Feb 3, 2026
996db5a
Incorporate feedback from review
mattt Feb 3, 2026
3659992
Conform internal GenerationSchema.Node to Equatable
mattt Feb 3, 2026
e76f048
Refactor and reorganize GenerationSchema
mattt Feb 3, 2026
87ef375
Rework StructuredGeneration adding docs and tests
mattt Feb 3, 2026
2564d59
Incorporate feedback from review
mattt Feb 3, 2026
5e5ed58
Update GenerableMacro to build guides using .minimum, .maximum, .rang…
mattt Feb 3, 2026
42e5934
Lower APIs from package to internal access
mattt Feb 3, 2026
4768a98
Change var to let
mattt Feb 3, 2026
dfb2f5d
Adjust constrained generation to never allow end tokens as valid term…
mattt Feb 3, 2026
c0cc2a3
Tighten free-string budget so we don’t exhaust the token budget on mu…
mattt Feb 3, 2026
8e46927
Fix eos token logic
mattt Feb 3, 2026
27f39b6
Incorporate feedback from review
mattt Feb 4, 2026
5d10c2a
Fix parsing of guide constraints without a description string
mattt Feb 4, 2026
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
11 changes: 10 additions & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Sources/AnyLanguageModel/Extensions/Character+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
extension Character {
static let jsonQuoteScalars: Set<UInt32> = [0x22, 0x201C, 0x201D, 0x2018, 0x2019]
static let jsonAllowedWhitespaceCharacters: Set<Character> = [" ", "\t", "\n"]

var containsEmojiScalar: Bool {
unicodeScalars.contains { scalar in
scalar.properties.isEmojiPresentation || scalar.properties.isEmoji
}
}

var isValidJSONStringCharacter: Bool {
guard self != "\\" else { return false }
guard let scalar = unicodeScalars.first, scalar.value >= 0x20 else { return false }
guard !Self.jsonQuoteScalars.contains(scalar.value) else { return false }

if let ascii = asciiValue {
let char = Character(UnicodeScalar(ascii))
if Self.jsonAllowedWhitespaceCharacters.contains(char) { return true }
return isLetter || isNumber || (isASCII && (isPunctuation || isSymbol))
}

// Allow non-ASCII letters/numbers and emoji, but disallow non-ASCII punctuation (e.g. "】")
return isLetter || isNumber || containsEmojiScalar
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

extension JSONDecoder.DateDecodingStrategy {
package static let iso8601WithFractionalSeconds = custom { decoder in
static let iso8601WithFractionalSeconds = custom { decoder in
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)

Expand Down
10 changes: 5 additions & 5 deletions Sources/AnyLanguageModel/Extensions/URLSession+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import JSONSchema
import FoundationNetworking
#endif

package enum HTTP {
package enum Method: String {
enum HTTP {
enum Method: String {
case get = "GET"
case post = "POST"
}
}

extension URLSession {
package func fetch<T: Decodable>(
func fetch<T: Decodable>(
_ method: HTTP.Method,
url: URL,
headers: [String: String] = [:],
Expand Down Expand Up @@ -57,7 +57,7 @@ extension URLSession {
}
}

package func fetchStream<T: Decodable & Sendable>(
func fetchStream<T: Decodable & Sendable>(
_ method: HTTP.Method,
url: URL,
headers: [String: String] = [:],
Expand Down Expand Up @@ -120,7 +120,7 @@ extension URLSession {
}
}

package func fetchEventStream<T: Decodable & Sendable>(
func fetchEventStream<T: Decodable & Sendable>(
_ method: HTTP.Method,
url: URL,
headers: [String: String] = [:],
Expand Down
66 changes: 42 additions & 24 deletions Sources/AnyLanguageModel/GenerationGuide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,24 @@ import struct Foundation.Decimal
import class Foundation.NSDecimalNumber

/// Guides that control how values are generated.
public struct GenerationGuide<Value> {}
public struct GenerationGuide<Value>: Sendable {
var minimumCount: Int?
var maximumCount: Int?
var minimum: Double?
var maximum: Double?

public init() {}

init(minimumCount: Int?, maximumCount: Int?) {
self.minimumCount = minimumCount
self.maximumCount = maximumCount
}

init(minimum: Double?, maximum: Double?) {
self.minimum = minimum
self.maximum = maximum
}
}

// MARK: - String Guides

Expand Down Expand Up @@ -45,7 +62,7 @@ extension GenerationGuide where Value == Int {
/// }
/// ```
public static func minimum(_ value: Int) -> GenerationGuide<Int> {
GenerationGuide<Int>()
GenerationGuide<Int>(minimum: Double(value), maximum: nil)
}

/// Enforces a maximum value.
Expand All @@ -65,7 +82,7 @@ extension GenerationGuide where Value == Int {
/// }
/// ```
public static func maximum(_ value: Int) -> GenerationGuide<Int> {
GenerationGuide<Int>()
GenerationGuide<Int>(minimum: nil, maximum: Double(value))
}

/// Enforces values fall within a range.
Expand All @@ -85,7 +102,7 @@ extension GenerationGuide where Value == Int {
/// }
/// ```
public static func range(_ range: ClosedRange<Int>) -> GenerationGuide<Int> {
GenerationGuide<Int>()
GenerationGuide<Int>(minimum: Double(range.lowerBound), maximum: Double(range.upperBound))
}
}

Expand All @@ -97,19 +114,19 @@ extension GenerationGuide where Value == Float {
///
/// The bounds are inclusive.
public static func minimum(_ value: Float) -> GenerationGuide<Float> {
GenerationGuide<Float>()
GenerationGuide<Float>(minimum: Double(value), maximum: nil)
}

/// Enforces a maximum value.
///
/// The bounds are inclusive.
public static func maximum(_ value: Float) -> GenerationGuide<Float> {
GenerationGuide<Float>()
GenerationGuide<Float>(minimum: nil, maximum: Double(value))
}

/// Enforces values fall within a range.
public static func range(_ range: ClosedRange<Float>) -> GenerationGuide<Float> {
GenerationGuide<Float>()
GenerationGuide<Float>(minimum: Double(range.lowerBound), maximum: Double(range.upperBound))
}
}

Expand All @@ -121,19 +138,22 @@ extension GenerationGuide where Value == Decimal {
///
/// The bounds are inclusive.
public static func minimum(_ value: Decimal) -> GenerationGuide<Decimal> {
GenerationGuide<Decimal>()
GenerationGuide<Decimal>(minimum: NSDecimalNumber(decimal: value).doubleValue, maximum: nil)
}

/// Enforces a maximum value.
///
/// The bounds are inclusive.
public static func maximum(_ value: Decimal) -> GenerationGuide<Decimal> {
GenerationGuide<Decimal>()
GenerationGuide<Decimal>(minimum: nil, maximum: NSDecimalNumber(decimal: value).doubleValue)
}

/// Enforces values fall within a range.
public static func range(_ range: ClosedRange<Decimal>) -> GenerationGuide<Decimal> {
GenerationGuide<Decimal>()
GenerationGuide<Decimal>(
minimum: NSDecimalNumber(decimal: range.lowerBound).doubleValue,
maximum: NSDecimalNumber(decimal: range.upperBound).doubleValue
)
}
}

Expand All @@ -144,18 +164,18 @@ extension GenerationGuide where Value == Double {
/// Enforces a minimum value.
/// The bounds are inclusive.
public static func minimum(_ value: Double) -> GenerationGuide<Double> {
GenerationGuide<Double>()
GenerationGuide<Double>(minimum: value, maximum: nil)
}

/// Enforces a maximum value.
/// The bounds are inclusive.
public static func maximum(_ value: Double) -> GenerationGuide<Double> {
GenerationGuide<Double>()
GenerationGuide<Double>(minimum: nil, maximum: value)
}

/// Enforces values fall within a range.
public static func range(_ range: ClosedRange<Double>) -> GenerationGuide<Double> {
GenerationGuide<Double>()
GenerationGuide<Double>(minimum: range.lowerBound, maximum: range.upperBound)
}
}

Expand All @@ -168,33 +188,31 @@ extension GenerationGuide {
/// The bounds are inclusive.
public static func minimumCount<Element>(_ count: Int) -> GenerationGuide<[Element]>
where Value == [Element] {
GenerationGuide<[Element]>()
GenerationGuide<[Element]>(minimumCount: count, maximumCount: nil)
}

/// Enforces a maximum number of elements in the array.
///
/// The bounds are inclusive.
public static func maximumCount<Element>(_ count: Int) -> GenerationGuide<[Element]>
where Value == [Element] {
GenerationGuide<[Element]>()
GenerationGuide<[Element]>(minimumCount: nil, maximumCount: count)
}

/// Enforces that the number of elements in the array fall within a closed range.
public static func count<Element>(_ range: ClosedRange<Int>) -> GenerationGuide<[Element]>
where Value == [Element] {
GenerationGuide<[Element]>()
GenerationGuide<[Element]>(minimumCount: range.lowerBound, maximumCount: range.upperBound)
}

/// Enforces that the array has exactly a certain number elements.
public static func count<Element>(_ count: Int) -> GenerationGuide<[Element]>
where Value == [Element] {
GenerationGuide<[Element]>()
GenerationGuide<[Element]>(minimumCount: count, maximumCount: count)
}

/// Enforces a guide on the elements within the array.
public static func element<Element>(_ guide: GenerationGuide<Element>) -> GenerationGuide<
[Element]
>
public static func element<Element>(_ guide: GenerationGuide<Element>) -> GenerationGuide<[Element]>
where Value == [Element] {
GenerationGuide<[Element]>()
}
Expand All @@ -210,7 +228,7 @@ extension GenerationGuide where Value == [Never] {
///
/// - Warning: This overload is only used for macro expansion. Don't call `GenerationGuide<[Never]>.minimumCount(_:)` on your own.
public static func minimumCount(_ count: Int) -> GenerationGuide<Value> {
GenerationGuide<Value>()
GenerationGuide<Value>(minimumCount: count, maximumCount: nil)
}

/// Enforces a maximum number of elements in the array.
Expand All @@ -219,20 +237,20 @@ extension GenerationGuide where Value == [Never] {
///
/// - Warning: This overload is only used for macro expansion. Don't call `GenerationGuide<[Never]>.maximumCount(_:)` on your own.
public static func maximumCount(_ count: Int) -> GenerationGuide<Value> {
GenerationGuide<Value>()
GenerationGuide<Value>(minimumCount: nil, maximumCount: count)
}

/// Enforces that the number of elements in the array fall within a closed range.
///
/// - Warning: This overload is only used for macro expansion. Don't call `GenerationGuide<[Never]>.count(_:)` on your own.
public static func count(_ range: ClosedRange<Int>) -> GenerationGuide<Value> {
GenerationGuide<Value>()
GenerationGuide<Value>(minimumCount: range.lowerBound, maximumCount: range.upperBound)
}

/// Enforces that the array has exactly a certain number elements.
///
/// - Warning: This overload is only used for macro expansion. Don't call `GenerationGuide<[Never]>.count(_:)` on your own.
public static func count(_ count: Int) -> GenerationGuide<Value> {
GenerationGuide<Value>()
GenerationGuide<Value>(minimumCount: count, maximumCount: count)
}
}
Loading