Skip to content

Commit b751579

Browse files
committed
wip
1 parent c03e505 commit b751579

File tree

4 files changed

+516
-242
lines changed

4 files changed

+516
-242
lines changed

Sources/Core/Models/JSONSchema.swift

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/// Represents a JSON Schema for validating JSON data structures.
2+
public indirect enum JSONSchema: Equatable, Hashable, Sendable {
3+
/// Represents the format of a string in JSON Schema.
4+
public enum StringFormat: String, Codable, Hashable, Equatable, Sendable {
5+
case ipv4, ipv6, uuid, date, time, email, duration, hostname, dateTime = "date-time"
6+
}
7+
8+
case null(description: String? = nil)
9+
case boolean(description: String? = nil)
10+
case anyOf([JSONSchema], description: String? = nil)
11+
case `enum`(cases: [String], description: String? = nil)
12+
case object(properties: [String: JSONSchema], description: String? = nil)
13+
case string(pattern: String? = nil, format: StringFormat? = nil, description: String? = nil)
14+
case array(of: JSONSchema, minItems: Int? = nil, maxItems: Int? = nil, description: String? = nil)
15+
case number(
16+
multipleOf: Int? = nil,
17+
minimum: Int? = nil,
18+
exclusiveMinimum: Int? = nil,
19+
maximum: Int? = nil,
20+
exclusiveMaximum: Int? = nil,
21+
description: String? = nil
22+
)
23+
case integer(
24+
multipleOf: Int? = nil,
25+
minimum: Int? = nil,
26+
exclusiveMinimum: Int? = nil,
27+
maximum: Int? = nil,
28+
exclusiveMaximum: Int? = nil,
29+
description: String? = nil
30+
)
31+
32+
var description: String? {
33+
switch self {
34+
case let .null(description),
35+
let .boolean(description),
36+
let .anyOf(_, description),
37+
let .enum(_, description),
38+
let .object(_, description),
39+
let .string(_, _, description),
40+
let .array(_, _, _, description),
41+
let .number(_, _, _, _, _, description),
42+
let .integer(_, _, _, _, _, description): return description
43+
}
44+
}
45+
46+
func withDescription(_: String?) -> JSONSchema {
47+
switch self {
48+
case .null: return .null(description: description)
49+
case .boolean: return .boolean(description: description)
50+
case let .anyOf(cases, _): return .anyOf(cases, description: description)
51+
case let .enum(cases, _): return .enum(cases: cases, description: description)
52+
case let .object(properties, _): return .object(properties: properties, description: description)
53+
case let .string(pattern, format, _): return .string(pattern: pattern, format: format, description: description)
54+
case let .array(of: items, minItems, maxItems, _):
55+
return .array(of: items, minItems: minItems, maxItems: maxItems, description: description)
56+
case let .number(multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum, _):
57+
return .number(
58+
multipleOf: multipleOf, minimum: minimum, exclusiveMinimum: exclusiveMinimum,
59+
maximum: maximum, exclusiveMaximum: exclusiveMaximum, description: description
60+
)
61+
case let .integer(multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum, _):
62+
return .integer(
63+
multipleOf: multipleOf, minimum: minimum, exclusiveMinimum: exclusiveMinimum,
64+
maximum: maximum, exclusiveMaximum: exclusiveMaximum, description: description
65+
)
66+
}
67+
}
68+
}
69+
70+
extension JSONSchema: Codable {
71+
private enum CodingKeys: String, CodingKey {
72+
case type, items, `enum`, anyOf, format, pattern, required, minItems, maxItems, minimum, maximum,
73+
properties, multipleOf, description, exclusiveMinimum, exclusiveMaximum, additionalProperties
74+
}
75+
76+
public func encode(to encoder: Encoder) throws {
77+
var container = encoder.container(keyedBy: CodingKeys.self)
78+
79+
switch self {
80+
case let .null(description):
81+
try container.encode("null", forKey: .type)
82+
if let description = description { try container.encode(description, forKey: .description) }
83+
case let .boolean(description):
84+
try container.encode("boolean", forKey: .type)
85+
if let description = description { try container.encode(description, forKey: .description) }
86+
case let .anyOf(cases, description):
87+
try container.encode(cases, forKey: .anyOf)
88+
if let description = description { try container.encode(description, forKey: .description) }
89+
case let .enum(cases, description):
90+
try container.encode(cases, forKey: .enum)
91+
try container.encode("string", forKey: .type)
92+
if let description { try container.encode(description, forKey: .description) }
93+
case let .object(properties, description):
94+
try container.encode("object", forKey: .type)
95+
try container.encode(properties, forKey: .properties)
96+
try container.encode(false, forKey: .additionalProperties)
97+
try container.encode(Array(properties.keys), forKey: .required)
98+
if let description { try container.encode(description, forKey: .description) }
99+
case let .string(pattern, format, description):
100+
try container.encode("string", forKey: .type)
101+
if let pattern = pattern { try container.encode(pattern, forKey: .pattern) }
102+
if let description { try container.encode(description, forKey: .description) }
103+
if let format = format { try container.encode(format.rawValue, forKey: .format) }
104+
case let .array(of: items, minItems, maxItems, description):
105+
try container.encode(items, forKey: .items)
106+
try container.encode("array", forKey: .type)
107+
if let description { try container.encode(description, forKey: .description) }
108+
if let minItems = minItems { try container.encode(minItems, forKey: .minItems) }
109+
if let maxItems = maxItems { try container.encode(maxItems, forKey: .maxItems) }
110+
case let .number(multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum, description):
111+
try container.encode("number", forKey: .type)
112+
if let minimum = minimum { try container.encode(minimum, forKey: .minimum) }
113+
if let maximum = maximum { try container.encode(maximum, forKey: .maximum) }
114+
if let description { try container.encode(description, forKey: .description) }
115+
if let multipleOf = multipleOf { try container.encode(multipleOf, forKey: .multipleOf) }
116+
if let exclusiveMaximum = exclusiveMaximum { try container.encode(exclusiveMaximum, forKey: .exclusiveMaximum) }
117+
if let exclusiveMinimum = exclusiveMinimum { try container.encode(exclusiveMinimum, forKey: .exclusiveMinimum) }
118+
case let .integer(multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum, description):
119+
try container.encode("integer", forKey: .type)
120+
if let minimum = minimum { try container.encode(minimum, forKey: .minimum) }
121+
if let maximum = maximum { try container.encode(maximum, forKey: .maximum) }
122+
if let description { try container.encode(description, forKey: .description) }
123+
if let multipleOf = multipleOf { try container.encode(multipleOf, forKey: .multipleOf) }
124+
if let exclusiveMaximum = exclusiveMaximum { try container.encode(exclusiveMaximum, forKey: .exclusiveMaximum) }
125+
if let exclusiveMinimum = exclusiveMinimum { try container.encode(exclusiveMinimum, forKey: .exclusiveMinimum) }
126+
}
127+
}
128+
129+
public init(from decoder: any Decoder) throws {
130+
let container = try decoder.container(keyedBy: CodingKeys.self)
131+
let description = try container.decodeIfPresent(String.self, forKey: .description)
132+
133+
if let anyOf = try container.decodeIfPresent([JSONSchema].self, forKey: .anyOf) {
134+
self = .anyOf(anyOf, description: description)
135+
return
136+
}
137+
138+
let type = try container.decode(String.self, forKey: .type)
139+
140+
if type == "null" {
141+
self = .null(description: description)
142+
return
143+
}
144+
145+
if type == "boolean" {
146+
self = .boolean(description: description)
147+
return
148+
}
149+
150+
if type == "object" {
151+
self = try .object(
152+
properties: container.decode([String: JSONSchema].self, forKey: .properties),
153+
description: description
154+
)
155+
return
156+
}
157+
158+
if type == "string", let enumCases = try container.decodeIfPresent([String].self, forKey: .enum) {
159+
self = .enum(cases: enumCases, description: description)
160+
return
161+
}
162+
163+
if type == "string" {
164+
self = try .string(
165+
pattern: container.decodeIfPresent(String.self, forKey: .pattern),
166+
format: container.decodeIfPresent(StringFormat.self, forKey: .format),
167+
description: description
168+
)
169+
return
170+
}
171+
172+
if type == "array" {
173+
self = try .array(
174+
of: container.decode(JSONSchema.self, forKey: .items),
175+
minItems: container.decodeIfPresent(Int.self, forKey: .minItems),
176+
maxItems: container.decodeIfPresent(Int.self, forKey: .maxItems),
177+
description: description
178+
)
179+
return
180+
}
181+
182+
if type == "number" {
183+
self = try .number(
184+
multipleOf: container.decodeIfPresent(Int.self, forKey: .multipleOf),
185+
minimum: container.decodeIfPresent(Int.self, forKey: .minimum),
186+
exclusiveMinimum: container.decodeIfPresent(Int.self, forKey: .exclusiveMinimum),
187+
maximum: container.decodeIfPresent(Int.self, forKey: .maximum),
188+
exclusiveMaximum: container.decodeIfPresent(Int.self, forKey: .exclusiveMaximum),
189+
description: description
190+
)
191+
return
192+
}
193+
194+
if type == "integer" {
195+
self = try .integer(
196+
multipleOf: container.decodeIfPresent(Int.self, forKey: .multipleOf),
197+
minimum: container.decodeIfPresent(Int.self, forKey: .minimum),
198+
exclusiveMinimum: container.decodeIfPresent(Int.self, forKey: .exclusiveMinimum),
199+
maximum: container.decodeIfPresent(Int.self, forKey: .maximum),
200+
exclusiveMaximum: container.decodeIfPresent(Int.self, forKey: .exclusiveMaximum),
201+
description: description
202+
)
203+
return
204+
}
205+
206+
throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "Unsupported schema type")
207+
}
208+
}

Sources/Core/Models/Response.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ public struct Response: Identifiable, Equatable, Hashable, Codable, Sendable {
2020
public let outputAudioFormat: Session.AudioFormat
2121

2222
/// Tools (functions) available to the model.
23-
public let tools: [Session.Tool]
23+
public let tools: [Tool]
2424

2525
/// How the model chooses tools.
26-
public let toolChoice: Session.ToolChoice
26+
public let toolChoice: Tool.Choice
2727

2828
/// Sampling temperature.
2929
public let temperature: Double
@@ -40,7 +40,7 @@ public struct Response: Identifiable, Equatable, Hashable, Codable, Sendable {
4040
/// Input items to include in the prompt for the model. Creates a new context for this response, without including the default conversation. Can include references to items from the default conversation.
4141
public let input: [Item]?
4242

43-
public init(modalities: [Session.Modality], instructions: String, voice: Session.Voice, outputAudioFormat: Session.AudioFormat, tools: [Session.Tool], toolChoice: Session.ToolChoice, temperature: Double, maxResponseOutputTokens: Int?, conversation: Conversation?, metadata: [String: String]?, input: [Item]?) {
43+
public init(modalities: [Session.Modality], instructions: String, voice: Session.Voice, outputAudioFormat: Session.AudioFormat, tools: [Tool], toolChoice: Tool.Choice, temperature: Double, maxResponseOutputTokens: Int?, conversation: Conversation?, metadata: [String: String]?, input: [Item]?) {
4444
self.modalities = modalities
4545
self.instructions = instructions
4646
self.voice = voice

0 commit comments

Comments
 (0)