Skip to content

Commit

Permalink
JSON schema reflection helper - explicit types
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbanda committed Sep 23, 2024
1 parent 4fb93f1 commit 516fb2d
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import io.cequence.openaiscala.anthropic.domain.response.{
}
import io.cequence.openaiscala.anthropic.domain.{ChatRole, Content, Message}
import io.cequence.wsclient.JsonUtil
import play.api.libs.functional.syntax._
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json._

object JsonFormats extends JsonFormats

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

implicit val textBlockWrites: Writes[TextBlock] = Json.writes[TextBlock]
implicit val imageBlockWrites: Writes[ImageBlock] = new Writes[ImageBlock] {
def writes(block: ImageBlock): JsValue = Json.obj(
"type" -> "image",
"source" -> Json.obj(
"type" -> block.`type`,
"media_type" -> block.mediaType,
"data" -> block.data
implicit val imageBlockWrites: Writes[ImageBlock] =
(block: ImageBlock) =>
Json.obj(
"type" -> "image",
"source" -> Json.obj(
"type" -> block.`type`,
"media_type" -> block.mediaType,
"data" -> block.data
)
)
)
}

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

implicit val contentBlockReads: Reads[ContentBlock] = new Reads[ContentBlock] {
def reads(json: JsValue): JsResult[ContentBlock] = {
implicit val contentBlockReads: Reads[ContentBlock] =
(json: JsValue) => {
(json \ "type").validate[String].flatMap {
case "text" => (json \ "text").validate[String].map(TextBlock.apply)
case "image" =>
Expand All @@ -81,7 +79,6 @@ trait JsonFormats {
case _ => JsError("Unsupported or invalid content block")
}
}
}

implicit val contentReads: Reads[Content] = new Reads[Content] {
def reads(json: JsValue): JsResult[Content] = json match {
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ val scala3 = "3.2.2"

ThisBuild / organization := "io.cequence"
ThisBuild / scalaVersion := scala212
ThisBuild / version := "1.1.0.RC.2"
ThisBuild / version := "1.1.0.RC.23"
ThisBuild / isSnapshot := false

lazy val commonSettings = Seq(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ trait JsonSchemaReflectionHelper {

def jsonSchemaFor[T: TypeTag](
dateAsNumber: Boolean = false,
useRuntimeMirror: Boolean = false
useRuntimeMirror: Boolean = false,
explicitTypes: Map[String, JsonSchema] = Map()
): JsonSchema = {
val mirror =
if (useRuntimeMirror) runtimeMirror(getClass.getClassLoader) else typeTag[T].mirror
asJsonSchema(typeOf[T], mirror, dateAsNumber)
asJsonSchema(typeOf[T], mirror, dateAsNumber, explicitTypes)
}

private def asJsonSchema(
typ: Type,
mirror: Mirror,
dateAsNumber: Boolean = false
dateAsNumber: Boolean,
explicitTypes: Map[String, JsonSchema]
): JsonSchema =
typ match {
// number
Expand All @@ -40,7 +42,13 @@ trait JsonSchemaReflectionHelper {
JsonSchema.String()

// enum
case t if t subMatches (typeOf[Enumeration#Value], typeOf[Enum[_]]) =>
case t if t subMatches typeOf[Enumeration#Value] =>
// TODO
// val enumValues = t.enumValues()
JsonSchema.String()

// java enum
case t if t subMatches typeOf[Enum[_]] =>
JsonSchema.String()

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

case t if t.isCaseClass() =>
caseClassAsJsonSchema(t, mirror, dateAsNumber)
caseClassAsJsonSchema(t, mirror, dateAsNumber, explicitTypes)

// map - TODO
case t if t subMatches (typeOf[Map[String, _]]) =>
Expand All @@ -81,14 +89,16 @@ trait JsonSchemaReflectionHelper {
private def caseClassAsJsonSchema(
typ: Type,
mirror: Mirror,
dateAsNumber: Boolean
dateAsNumber: Boolean,
explicitTypes: Map[String, JsonSchema]
): JsonSchema = {
val memberNamesAndTypes = typ.getCaseClassFields()

val fieldSchemas = memberNamesAndTypes.toSeq.map {
case (fieldName: String, memberType: Type) =>
val fieldSchema = asJsonSchema(memberType, mirror, dateAsNumber)
(fieldName, fieldSchema, memberType.isOption())
val implicitFieldSchema = asJsonSchema(memberType, mirror, dateAsNumber, explicitTypes)
val explicitFieldSchema = explicitTypes.get(fieldName)
(fieldName, explicitFieldSchema.getOrElse(implicitFieldSchema), memberType.isOption())
}

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

0 comments on commit 516fb2d

Please sign in to comment.