diff --git a/openai-client/src/main/scala/io/cequence/openaiscala/JsonFormats.scala b/openai-client/src/main/scala/io/cequence/openaiscala/JsonFormats.scala index 8dafbc50..ddaec7fe 100644 --- a/openai-client/src/main/scala/io/cequence/openaiscala/JsonFormats.scala +++ b/openai-client/src/main/scala/io/cequence/openaiscala/JsonFormats.scala @@ -311,8 +311,38 @@ object JsonFormats { Format(reads, writes) } + implicit val byteArrayReads: Reads[Seq[Byte]] = new Reads[Seq[Byte]] { + + /** + * Parses a JSON representation of a `Seq[Byte]` into a `JsResult[Seq[Byte]]`. This method + * expects the JSON to be an array of numbers, where each number represents a valid byte + * value (between -128 and 127, inclusive). If the JSON structure is correct and all + * numbers are valid byte values, it returns a `JsSuccess` containing the sequence of + * bytes. Otherwise, it returns a `JsError` detailing the parsing issue encountered. + * + * @param json + * The `JsValue` to be parsed, expected to be a `JsArray` of `JsNumber`. + * @return + * A `JsResult[Seq[Byte]]` which is either a `JsSuccess` containing the parsed sequence + * of bytes, or a `JsError` with parsing error details. + */ + def reads(json: JsValue): JsResult[Seq[Byte]] = json match { + case JsArray(elements) => + try { + JsSuccess(elements.map { + case JsNumber(n) if n.isValidInt => n.toIntExact.toByte + case _ => throw new RuntimeException("Invalid byte value") + }.toIndexedSeq) + } catch { + case e: Exception => JsError("Error parsing byte array: " + e.getMessage) + } + case _ => JsError("Expected JSON array for byte array") + } + } + implicit lazy val logprobInfoFormat: Format[LogprobInfo] = Json.format[LogprobInfo] + implicit lazy val logprobsFormat: Format[Logprobs] = Json.format[Logprobs] diff --git a/openai-examples/src/main/scala/io/cequence/openaiscala/examples/CreateChatCompletionWithLogprobsWithSpecialChars.scala b/openai-examples/src/main/scala/io/cequence/openaiscala/examples/CreateChatCompletionWithLogprobsWithSpecialChars.scala new file mode 100644 index 00000000..4892a098 --- /dev/null +++ b/openai-examples/src/main/scala/io/cequence/openaiscala/examples/CreateChatCompletionWithLogprobsWithSpecialChars.scala @@ -0,0 +1,33 @@ +package io.cequence.openaiscala.examples + +import io.cequence.openaiscala.domain._ +import io.cequence.openaiscala.domain.settings.CreateChatCompletionSettings + +import scala.concurrent.Future + +object CreateChatCompletionWithLogprobsWithSpecialChars extends Example { + + val messages = Seq(UserMessage("Just return the string °C and nothing else")) + + override protected def run: Future[_] = + service + .createChatCompletion( + messages = messages, + settings = CreateChatCompletionSettings( + model = ModelId.gpt_4o, + temperature = Some(0), + max_tokens = Some(100), + logprobs = Some(true), + top_logprobs = Some(3) + ) + ) + .map { response => + printMessageContent(response) + val logprobs = response.choices.head.logprobs.map(_.content).getOrElse(Nil) + logprobs.foreach { logprob => + println( + s"Logprob: ${logprob.token} -> ${logprob.logprob}, top: ${logprob.top_logprobs.map(_.token).mkString(", ")}" + ) + } + } +}