Skip to content

Commit 516fb2d

Browse files
committed
JSON schema reflection helper - explicit types
1 parent 4fb93f1 commit 516fb2d

File tree

3 files changed

+37
-30
lines changed

3 files changed

+37
-30
lines changed

anthropic-client/src/main/scala/io/cequence/openaiscala/anthropic/JsonFormats.scala

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import io.cequence.openaiscala.anthropic.domain.response.{
2121
}
2222
import io.cequence.openaiscala.anthropic.domain.{ChatRole, Content, Message}
2323
import io.cequence.wsclient.JsonUtil
24-
import play.api.libs.functional.syntax._
25-
import play.api.libs.json._
24+
import play.api.libs.functional.syntax._
25+
import play.api.libs.json._
2626

2727
object JsonFormats extends JsonFormats
2828

@@ -48,27 +48,25 @@ trait JsonFormats {
4848
implicit val textBlockReads: Reads[TextBlock] = Json.reads[TextBlock]
4949

5050
implicit val textBlockWrites: Writes[TextBlock] = Json.writes[TextBlock]
51-
implicit val imageBlockWrites: Writes[ImageBlock] = new Writes[ImageBlock] {
52-
def writes(block: ImageBlock): JsValue = Json.obj(
53-
"type" -> "image",
54-
"source" -> Json.obj(
55-
"type" -> block.`type`,
56-
"media_type" -> block.mediaType,
57-
"data" -> block.data
51+
implicit val imageBlockWrites: Writes[ImageBlock] =
52+
(block: ImageBlock) =>
53+
Json.obj(
54+
"type" -> "image",
55+
"source" -> Json.obj(
56+
"type" -> block.`type`,
57+
"media_type" -> block.mediaType,
58+
"data" -> block.data
59+
)
5860
)
59-
)
60-
}
6161

62-
implicit val contentBlockWrites: Writes[ContentBlock] = new Writes[ContentBlock] {
63-
def writes(block: ContentBlock): JsValue = block match {
64-
case tb: TextBlock =>
65-
Json.obj("type" -> "text") ++ Json.toJson(tb)(textBlockWrites).as[JsObject]
66-
case ib: ImageBlock => Json.toJson(ib)(imageBlockWrites)
67-
}
62+
implicit val contentBlockWrites: Writes[ContentBlock] = {
63+
case tb: TextBlock =>
64+
Json.obj("type" -> "text") ++ Json.toJson(tb)(textBlockWrites).as[JsObject]
65+
case ib: ImageBlock => Json.toJson(ib)(imageBlockWrites)
6866
}
6967

70-
implicit val contentBlockReads: Reads[ContentBlock] = new Reads[ContentBlock] {
71-
def reads(json: JsValue): JsResult[ContentBlock] = {
68+
implicit val contentBlockReads: Reads[ContentBlock] =
69+
(json: JsValue) => {
7270
(json \ "type").validate[String].flatMap {
7371
case "text" => (json \ "text").validate[String].map(TextBlock.apply)
7472
case "image" =>
@@ -81,7 +79,6 @@ trait JsonFormats {
8179
case _ => JsError("Unsupported or invalid content block")
8280
}
8381
}
84-
}
8582

8683
implicit val contentReads: Reads[Content] = new Reads[Content] {
8784
def reads(json: JsValue): JsResult[Content] = json match {

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ val scala3 = "3.2.2"
77

88
ThisBuild / organization := "io.cequence"
99
ThisBuild / scalaVersion := scala212
10-
ThisBuild / version := "1.1.0.RC.2"
10+
ThisBuild / version := "1.1.0.RC.23"
1111
ThisBuild / isSnapshot := false
1212

1313
lazy val commonSettings = Seq(

openai-core/src/main/scala-2/io/cequence/openaiscala/service/JsonSchemaReflectionHelper.scala

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ trait JsonSchemaReflectionHelper {
1111

1212
def jsonSchemaFor[T: TypeTag](
1313
dateAsNumber: Boolean = false,
14-
useRuntimeMirror: Boolean = false
14+
useRuntimeMirror: Boolean = false,
15+
explicitTypes: Map[String, JsonSchema] = Map()
1516
): JsonSchema = {
1617
val mirror =
1718
if (useRuntimeMirror) runtimeMirror(getClass.getClassLoader) else typeTag[T].mirror
18-
asJsonSchema(typeOf[T], mirror, dateAsNumber)
19+
asJsonSchema(typeOf[T], mirror, dateAsNumber, explicitTypes)
1920
}
2021

2122
private def asJsonSchema(
2223
typ: Type,
2324
mirror: Mirror,
24-
dateAsNumber: Boolean = false
25+
dateAsNumber: Boolean,
26+
explicitTypes: Map[String, JsonSchema]
2527
): JsonSchema =
2628
typ match {
2729
// number
@@ -40,7 +42,13 @@ trait JsonSchemaReflectionHelper {
4042
JsonSchema.String()
4143

4244
// enum
43-
case t if t subMatches (typeOf[Enumeration#Value], typeOf[Enum[_]]) =>
45+
case t if t subMatches typeOf[Enumeration#Value] =>
46+
// TODO
47+
// val enumValues = t.enumValues()
48+
JsonSchema.String()
49+
50+
// java enum
51+
case t if t subMatches typeOf[Enum[_]] =>
4452
JsonSchema.String()
4553

4654
// date
@@ -50,11 +58,11 @@ trait JsonSchemaReflectionHelper {
5058
// array/seq
5159
case t if t subMatches (typeOf[Seq[_]], typeOf[Set[_]], typeOf[Array[_]]) =>
5260
val innerType = t.typeArgs.head
53-
val itemsSchema = asJsonSchema(innerType, mirror, dateAsNumber)
61+
val itemsSchema = asJsonSchema(innerType, mirror, dateAsNumber, explicitTypes)
5462
JsonSchema.Array(itemsSchema)
5563

5664
case t if t.isCaseClass() =>
57-
caseClassAsJsonSchema(t, mirror, dateAsNumber)
65+
caseClassAsJsonSchema(t, mirror, dateAsNumber, explicitTypes)
5866

5967
// map - TODO
6068
case t if t subMatches (typeOf[Map[String, _]]) =>
@@ -81,14 +89,16 @@ trait JsonSchemaReflectionHelper {
8189
private def caseClassAsJsonSchema(
8290
typ: Type,
8391
mirror: Mirror,
84-
dateAsNumber: Boolean
92+
dateAsNumber: Boolean,
93+
explicitTypes: Map[String, JsonSchema]
8594
): JsonSchema = {
8695
val memberNamesAndTypes = typ.getCaseClassFields()
8796

8897
val fieldSchemas = memberNamesAndTypes.toSeq.map {
8998
case (fieldName: String, memberType: Type) =>
90-
val fieldSchema = asJsonSchema(memberType, mirror, dateAsNumber)
91-
(fieldName, fieldSchema, memberType.isOption())
99+
val implicitFieldSchema = asJsonSchema(memberType, mirror, dateAsNumber, explicitTypes)
100+
val explicitFieldSchema = explicitTypes.get(fieldName)
101+
(fieldName, explicitFieldSchema.getOrElse(implicitFieldSchema), memberType.isOption())
92102
}
93103

94104
val required = fieldSchemas.collect { case (fieldName, _, false) => fieldName }

0 commit comments

Comments
 (0)