Skip to content

Commit e298a76

Browse files
committed
fix ThreadFullMessage serialization (when fields are missing)
1 parent 0c91d1b commit e298a76

File tree

6 files changed

+111
-42
lines changed

6 files changed

+111
-42
lines changed

openai-client/src/test/scala/io/cequence/openaiscala/JsonFormatsSpec.scala

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,52 @@ class JsonFormatsSpec extends AnyWordSpecLike with Matchers {
122122
| } ]
123123
|}""".stripMargin
124124

125+
private val ThreadMessageContentJson =
126+
"""{
127+
| "type": "text",
128+
| "text": {
129+
| "value": "Based on the conference program you provided, here are some talks that align with your goal of digitising and optimizing procurement processes with the use of AI:\n\n1. **\"AI AS A TOOL OR A TOY?\"** \n - Description: This session covers the basics of AI in the context of corporate procurement, successful case studies, AI tools for procurement managers, integrating AI into existing systems, and measuring success.\n - Speaker: Not specified\n - Time: 9:50-10:30, Stage 6 Chat GPT and Other AI in Procurement\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\n2. **\"HOW TO 'TALK' TO AI FOR BETTER PROCUREMENT DECISIONS\"** \n - Description: Focuses on formulating prompts for effective interaction with AI, adapting AI to enterprise processes, overcoming communication challenges, and includes interactive demonstrations.\n - Speaker: Not specified\n - Time: 10:30-11:10, Stage 6 Chat GPT and Other AI in Procurement\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\n3. **\"WHAT AI DOESN'T SAY / HOW IT AFFECTS CORPORATE PROCUREMENT ERRORS & BIASES IN AI: RECOGNISING & MINIMISING RISKS\"** \n - Description: Covers understanding AI decision-making processes, legal and ethical aspects, risk mitigation strategies, and DeepFake attacks.\n - Speaker: Not specified\n - Time: 11:30-12:10, Stage 6 Chat GPT and Other AI in Procurement\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\n4. **\"FUTURE OF PROCUREMENT ECHOES\"** \n - Description: Explores the application of AI in contract management and workflow to simplify procurement processes.\n - Speakers: Rasťo Kovaľ – Cequence, Jana Dubcová – CNPK\n - Time: 14:30-15:30, Stage 7 The Digital Revolution in Procurement Practice\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\nAttending these talks can provide valuable insights into leveraging AI for digital transformation and optimization of procurement processes.",
130+
| "annotations": [
131+
| {
132+
| "type": "file_citation",
133+
| "text": "【4:0†source】",
134+
| "start_index": 555,
135+
| "end_index": 567,
136+
| "file_citation": {
137+
| "file_id": "file-gGBbjfqasohuF31BhkIpmTZk"
138+
| }
139+
| },
140+
| {
141+
| "type": "file_citation",
142+
| "text": "【4:0†source】",
143+
| "start_index": 973,
144+
| "end_index": 985,
145+
| "file_citation": {
146+
| "file_id": "file-gGBbjfqasohuF31BhkIpmTZk"
147+
| }
148+
| },
149+
| {
150+
| "type": "file_citation",
151+
| "text": "【4:0†source】",
152+
| "start_index": 1398,
153+
| "end_index": 1410,
154+
| "file_citation": {
155+
| "file_id": "file-gGBbjfqasohuF31BhkIpmTZk"
156+
| }
157+
| },
158+
| {
159+
| "type": "file_citation",
160+
| "text": "【4:0†source】",
161+
| "start_index": 1754,
162+
| "end_index": 1766,
163+
| "file_citation": {
164+
| "file_id": "file-gGBbjfqasohuF31BhkIpmTZk"
165+
| }
166+
| }
167+
| ]
168+
| }
169+
|}""".stripMargin
170+
125171
"JSON Formats" should {
126172

127173
"serialize and deserialize a String response format" in {
@@ -321,12 +367,64 @@ class JsonFormatsSpec extends AnyWordSpecLike with Matchers {
321367
Pretty
322368
)
323369
}
370+
371+
"serialize and deserialize ThreadMessageContent" in {
372+
val threadMessageContent =
373+
ThreadMessageContent(
374+
`type` = ThreadMessageContentType.text,
375+
text = Some(
376+
ThreadMessageText(
377+
value =
378+
"Based on the conference program you provided, here are some talks that align with your goal of digitising and optimizing procurement processes with the use of AI:\n\n1. **\"AI AS A TOOL OR A TOY?\"** \n - Description: This session covers the basics of AI in the context of corporate procurement, successful case studies, AI tools for procurement managers, integrating AI into existing systems, and measuring success.\n - Speaker: Not specified\n - Time: 9:50-10:30, Stage 6 Chat GPT and Other AI in Procurement\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\n2. **\"HOW TO 'TALK' TO AI FOR BETTER PROCUREMENT DECISIONS\"** \n - Description: Focuses on formulating prompts for effective interaction with AI, adapting AI to enterprise processes, overcoming communication challenges, and includes interactive demonstrations.\n - Speaker: Not specified\n - Time: 10:30-11:10, Stage 6 Chat GPT and Other AI in Procurement\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\n3. **\"WHAT AI DOESN'T SAY / HOW IT AFFECTS CORPORATE PROCUREMENT ERRORS & BIASES IN AI: RECOGNISING & MINIMISING RISKS\"** \n - Description: Covers understanding AI decision-making processes, legal and ethical aspects, risk mitigation strategies, and DeepFake attacks.\n - Speaker: Not specified\n - Time: 11:30-12:10, Stage 6 Chat GPT and Other AI in Procurement\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\n4. **\"FUTURE OF PROCUREMENT ECHOES\"** \n - Description: Explores the application of AI in contract management and workflow to simplify procurement processes.\n - Speakers: Rasťo Kovaľ – Cequence, Jana Dubcová – CNPK\n - Time: 14:30-15:30, Stage 7 The Digital Revolution in Procurement Practice\n - Source: `file-gGBbjfqasohuF31BhkIpmTZk`【4:0†source】\n\nAttending these talks can provide valuable insights into leveraging AI for digital transformation and optimization of procurement processes.",
379+
annotations = Seq(
380+
FileAnnotation(
381+
FileAnnotationType.file_citation,
382+
text = "【4:0†source】",
383+
start_index = 555,
384+
end_index = 567,
385+
file_citation = Some(FileCitation("file-gGBbjfqasohuF31BhkIpmTZk"))
386+
),
387+
FileAnnotation(
388+
FileAnnotationType.file_citation,
389+
text = "【4:0†source】",
390+
start_index = 973,
391+
end_index = 985,
392+
file_citation = Some(FileCitation("file-gGBbjfqasohuF31BhkIpmTZk"))
393+
),
394+
FileAnnotation(
395+
FileAnnotationType.file_citation,
396+
text = "【4:0†source】",
397+
start_index = 1398,
398+
end_index = 1410,
399+
file_citation = Some(FileCitation("file-gGBbjfqasohuF31BhkIpmTZk"))
400+
),
401+
FileAnnotation(
402+
FileAnnotationType.file_citation,
403+
text = "【4:0†source】",
404+
start_index = 1754,
405+
end_index = 1766,
406+
file_citation = Some(FileCitation("file-gGBbjfqasohuF31BhkIpmTZk"))
407+
)
408+
)
409+
)
410+
)
411+
)
412+
413+
testCodec[ThreadMessageContent](
414+
threadMessageContent,
415+
ThreadMessageContentJson,
416+
Pretty,
417+
justSemantics = true
418+
)(threadMessageContentFormat)
419+
}
420+
324421
}
325422

326423
private def testCodec[A](
327424
value: A,
328425
json: String,
329-
printMode: JsonPrintMode = Compact
426+
printMode: JsonPrintMode = Compact,
427+
justSemantics: Boolean = false
330428
)(
331429
implicit format: Format[A]
332430
): Unit = {
@@ -335,7 +433,8 @@ class JsonFormatsSpec extends AnyWordSpecLike with Matchers {
335433
case Compact => jsValue.toString()
336434
case Pretty => Json.prettyPrint(jsValue)
337435
}
338-
serialized shouldBe json
436+
437+
if (!justSemantics) serialized shouldBe json
339438

340439
Json.parse(json).as[A] shouldBe value
341440
}

openai-core/src/main/scala/io/cequence/openaiscala/domain/ThreadFullMessage.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ case class ThreadFullMessage(
3939

4040
case class ThreadMessageContent(
4141
`type`: ThreadMessageContentType,
42-
image_file: Option[FileId],
42+
image_file: Option[FileId] = None,
4343
text: Option[ThreadMessageText]
4444
)
4545

@@ -58,7 +58,7 @@ case class ThreadMessageText(
5858
case class FileAnnotation(
5959
`type`: FileAnnotationType,
6060
file_citation: Option[FileCitation],
61-
file_path: Option[FileId],
61+
file_path: Option[FileId] = None,
6262
// The text in the message content that needs to be replaced.
6363
text: String,
6464
start_index: Int,
@@ -83,5 +83,5 @@ case class FileCitation(
8383
// The ID of the specific File the citation is from.
8484
file_id: String,
8585
// The specific quote in the file.
86-
quote: String
86+
quote: Option[String] = None
8787
)

openai-examples/src/main/scala/io/cequence/openaiscala/examples/CreateRunWithVectorStore.scala

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,13 @@ package io.cequence.openaiscala.examples
22

33
import io.cequence.openaiscala.domain.AssistantToolResource.FileSearchResources
44
import io.cequence.openaiscala.domain.response.FileInfo
5-
import io.cequence.openaiscala.domain.response.ResponseFormat.StringResponse
65
import io.cequence.openaiscala.domain.settings.CreateRunSettings
7-
import io.cequence.openaiscala.domain.{
8-
BaseMessage,
9-
FileSearchSpec,
10-
FunctionSpec,
11-
ModelId,
12-
RequiredAction,
13-
ThreadFullMessage,
14-
ThreadMessage,
15-
ThreadMessageContent,
16-
UserMessage,
17-
VectorStore
18-
}
19-
import io.cequence.openaiscala.examples.CreateRunWithVectorStore.vectorStoreId
20-
import io.cequence.openaiscala.examples.CreateVectorStore.service
21-
import io.cequence.openaiscala.examples.UploadFile.service
22-
import io.cequence.openaiscala.examples.adapter.RoundRobinAdapterExample.adapters
6+
import io.cequence.openaiscala.domain._
237
import io.cequence.openaiscala.service.adapter.OpenAIServiceAdapters
248
import io.cequence.openaiscala.service.{OpenAIService, OpenAIServiceFactory}
259

2610
import java.io.File
2711
import java.nio.file.Paths
28-
import scala.collection.immutable.ListMap
2912
import scala.concurrent.Future
3013

3114
object CreateRunWithVectorStore extends Example {
@@ -79,10 +62,11 @@ object CreateRunWithVectorStore extends Example {
7962
} yield thread
8063

8164
val vectorStoreId = "vs_6nTuNJKVytSoFke9nvnpptUZ" // createVectorStore(fileInfo).map(_.id)
65+
val assistantId = AssistantId("asst_gIharZ60V7hvf5pQvvjkw7Mf")
8266
override protected def run: Future[_] =
8367
for {
8468
// fileInfo <- uploadFile
85-
assistant <- createPlanner(vectorStoreId)
69+
// assistant <- createPlanner(vectorStoreId)
8670
eventsThread <- createSpecMessagesThread(vectorStoreId)
8771

8872
_ <- service.listThreadMessages(eventsThread.id).map { messages =>
@@ -94,9 +78,7 @@ object CreateRunWithVectorStore extends Example {
9478

9579
run <- service.createRun(
9680
threadId = eventsThread.id,
97-
assistantId = assistant.id,
98-
instructions = None,
99-
additionalMessages = Seq.empty,
81+
assistantId = assistantId,
10082
tools = Seq(FileSearchSpec),
10183
responseToolChoice = Some(RequiredAction.EnforcedTool(FileSearchSpec)),
10284
settings = CreateRunSettings(),

openai-examples/src/main/scala/io/cequence/openaiscala/examples/ListThreadMessages.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ object ListThreadMessages extends Example {
88
override protected def run: Future[Unit] =
99
for {
1010
messages <- service.listThreadMessages(
11-
// threadId = "thread_c6fFMmUw30l30SzG2KdUViMn",
12-
threadId = "thread_q5LqRp5snpGIG7yxZvUop2rj",
11+
threadId = "thread_VStezPO5449TyPgpkgeEtI81",
1312
Pagination.limit(5)
1413
)
1514
} yield {

openai-examples/src/main/scala/io/cequence/openaiscala/examples/RetrieveFile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object RetrieveFile extends Example {
66

77
override protected def run: Future[_] =
88
for {
9-
assistant <- service.retrieveFile("file-A9V1zO4XpjjqBke8Kdp78vMU")
9+
assistant <- service.retrieveFile("file-2bZn9Vu6WicoTMOAEGW92pml")
1010
} yield {
1111
println(assistant)
1212
}

openai-examples/src/main/scala/io/cequence/openaiscala/examples/RetrieveRun.scala

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.cequence.openaiscala.examples
22

33
import io.cequence.openaiscala.JsonFormats._
4-
import io.cequence.openaiscala.domain.Batch.Batch
54
import io.cequence.openaiscala.domain.Run
65
import play.api.libs.json.Json
76
import play.api.libs.json.Json.prettyPrint
@@ -12,17 +11,7 @@ object RetrieveRun extends Example {
1211

1312
override protected def run: Future[Option[Unit]] =
1413
service
15-
// .retrieveRun("thread_GvtoqKcdlD0vTECuuZgcxDfK", "run_RBgA0No2ivbqcNKGSFVCjkxy")
16-
// .retrieveRun("thread_YnAy1CQ51AkBmcI3XWO1zuUB", "run_teGlp0JtYh1QaFk73FtuHMAz")
17-
// .retrieveRun("thread_QCSvmg9HibS2fFJuUAcGNUBv", "run_1JXtEreahF4zrqjNssA3doLf")
18-
// .retrieveRun("thread_7fbLGrV3gNqNrgxbqx5LMq9i", "run_BTVtOS07o7NNpQpvDEAt0LVQ")
19-
// .retrieveRun("thread_2v2Limjle2axcp1Ye3aBdhtH", "run_UyGjyofIwLfs9GnJUmTBwf3Y")
20-
// .retrieveRun("thread_QJAdWNGgg2oU7a90S5dv5pxO", "run_zxKcUGmGZo4VQmLbCfDoVA7d")
21-
// .retrieveRun("thread_kw8vGZ5Gn0fNttA2iNYOgNm9", "run_oIH3FTd10vRmSK3Rsi1r1bhX")
22-
// .retrieveRun("thread_Kxj1Np8izlEQzxg7TlroziiC", "run_Lul9kHGzEzI1ee8CQREsWkYK")
23-
// .retrieveRun("thread_vEgr0e5WnoVMjHvxkhGz8WYw", "run_Z9OrPubVLFoQgWH8qEtRr4bg")
24-
// .retrieveRun("thread_JltAKvPHLqFt2WNgnCgU9ZFJ", "run_aBNmdmR8XzhzohSQlMdp443m")
25-
.retrieveRun("thread_q5LqRp5snpGIG7yxZvUop2rj", "run_sAPI05LAoUOULAtobHarIaGT")
14+
.retrieveRun("thread_VStezPO5449TyPgpkgeEtI81", "run_g1d7aSLHwDX14m9CU0RZs3Tg")
2615
.map { maybeRun =>
2716
println(maybeRun)
2817

0 commit comments

Comments
 (0)