Skip to content

Commit 3971257

Browse files
committed
Core - hasType trait and json format
1 parent 2a41601 commit 3971257

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

openai-core/src/main/scala/io/cequence/openaiscala/JsonFormats.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import io.cequence.openaiscala.domain.response.ResponseFormat.{
2727
TextResponse
2828
}
2929
import io.cequence.openaiscala.domain.response._
30-
import io.cequence.openaiscala.domain.{ThreadMessageFile, _}
30+
import io.cequence.openaiscala.domain.{HasType, ThreadMessageFile, _}
3131
import io.cequence.wsclient.JsonUtil
3232
import io.cequence.wsclient.JsonUtil.{enumFormat, snakeEnumFormat}
3333
import play.api.libs.functional.syntax._
@@ -1385,4 +1385,18 @@ object JsonFormats {
13851385
Format(eitherJsonSchemaReads, eitherJsonSchemaWrites)
13861386

13871387
implicit val jsonSchemaDefFormat: Format[JsonSchemaDef] = Json.format[JsonSchemaDef]
1388+
1389+
/**
1390+
* Wraps an OFormat with type field handling for classes extending HasType. Automatically
1391+
* adds the `type` field during serialization and validates it during deserialization.
1392+
*
1393+
* @param format
1394+
* The underlying OFormat to wrap
1395+
* @tparam T
1396+
* The type extending HasType
1397+
* @return
1398+
* A Format that handles type field serialization and validation
1399+
*/
1400+
def formatWithType[T <: HasType](format: OFormat[T]): OFormat[T] =
1401+
TypeJsonWrapper(format)
13881402
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.cequence.openaiscala
2+
3+
import io.cequence.openaiscala.domain.HasType
4+
import play.api.libs.json.{Format, JsError, JsObject, JsResult, JsSuccess, JsValue, Json, OFormat}
5+
6+
private class TypeJsonWrapper[T <: HasType](val format: OFormat[T]) extends OFormat[T] {
7+
override def reads(json: JsValue): JsResult[T] = {
8+
// First read using the underlying format
9+
format.reads(json).flatMap { obj =>
10+
// Validate that the type field in JSON matches the object's type
11+
(json \ "type").asOpt[String] match {
12+
case Some(jsonType) if jsonType == obj.`type`.toString =>
13+
JsSuccess(obj)
14+
case Some(jsonType) =>
15+
JsError(s"Type mismatch: expected ${obj.`type`.toString}, got $jsonType")
16+
case None =>
17+
JsError("Missing type field")
18+
}
19+
}
20+
}
21+
22+
override def writes(o: T): JsObject = {
23+
// Write using the underlying format, then add type field
24+
val json = format.writes(o)
25+
json ++ Json.obj("type" -> o.`type`.toString)
26+
}
27+
}
28+
29+
object TypeJsonWrapper {
30+
def apply[T <: HasType](format: OFormat[T]): OFormat[T] = new TypeJsonWrapper(format)
31+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.cequence.openaiscala.domain
2+
3+
trait HasType {
4+
val `type`: Any
5+
}

0 commit comments

Comments
 (0)