aallam / openai-kotlin

OpenAI API client for Kotlin with multiplatform and coroutines capabilities.
MIT License
1.5k stars 180 forks source link

[FEATURE REQUEST] Add Support for Content Filter Response Deserialization for azure async filter streaming #339

Open rasharab opened 6 months ago

rasharab commented 6 months ago

Description

Azure recently added async content filtering which outputs a filter annotation at end of streaming tokens with this structure: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/content-filter?tabs=warning%2Cpython-new#annotation-message

data: { 
    "id": "", 
    "object": "", 
    "created": 0, 
    "model": "", 
    "choices": [ 
        { 
            "index": 0, 
            "finish_reason": null, 
            "content_filter_results": { ... }, 
            "content_filter_raw": [ ... ], 
            "content_filter_offsets": { 
                "check_offset": 44, 
                "start_offset": 44, 
                "end_offset": 198 
            } 
        } 
    ], 
    "usage": null 
} 

We are currently unable to deserialize that due to this:

com.aallam.openai.api.exception.OpenAIHttpException: Field 'delta' is required for type with serial name 'com.aallam.openai.api.chat.ChatChunk', but it was missing at path: $.choices[0]
    at com.aallam.openai.client.internal.http.HttpTransport.handleException(HttpTransport.kt:52)
    at com.aallam.openai.client.internal.http.HttpTransport.perform(HttpTransport.kt:34)
    at com.aallam.openai.client.internal.http.HttpTransport$perform$2.invokeSuspend(HttpTransport.kt)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
    at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:111)
    at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:99)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:585)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:802)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:706)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:693)
Caused by: kotlinx.serialization.MissingFieldException: Field 'delta' is required for type with serial name 'com.aallam.openai.api.chat.ChatChunk', but it was missing at path: $.choices[0]
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:95)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:168)
    at kotlinx.serialization.encoding.CompositeDecoder$DefaultImpls.decodeSerializableElement$default(Decoding.kt:538)
    at kotlinx.serialization.internal.CollectionLikeSerializer.readElement(CollectionSerializers.kt:80)
    at kotlinx.serialization.internal.AbstractCollectionSerializer.readElement$default(CollectionSerializers.kt:51)
    at kotlinx.serialization.internal.AbstractCollectionSerializer.merge(CollectionSerializers.kt:36)
    at kotlinx.serialization.internal.AbstractCollectionSerializer.deserialize(CollectionSerializers.kt:43)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:168)
    at com.aallam.openai.api.chat.ChatCompletionChunk$$serializer.deserialize(ChatCompletionChunk.kt:13)
    at com.aallam.openai.api.chat.ChatCompletionChunk$$serializer.deserialize(ChatCompletionChunk.kt:13)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
    at kotlinx.serialization.json.Json.decodeFromString(Json.kt:107)
    at com.aallam.openai.client.internal.api.ChatApi$chatCompletions$1$1.invokeSuspend(ChatApi.kt:66)
    ... 8 common frames omitted
Caused by: kotlinx.serialization.MissingFieldException: Field 'delta' is required for type with serial name 'com.aallam.openai.api.chat.ChatChunk', but it was missing
    at kotlinx.serialization.internal.PluginExceptionsKt.throwMissingFieldException(PluginExceptions.kt:20)
    at com.aallam.openai.api.chat.ChatChunk.<init>(ChatChunk.kt:13)
    at com.aallam.openai.api.chat.ChatChunk.<init>(ChatChunk.kt)
    at com.aallam.openai.api.chat.ChatChunk$$serializer.deserialize(ChatChunk.kt:13)
    at com.aallam.openai.api.chat.ChatChunk$$serializer.deserialize(ChatChunk.kt:13)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
    ... 25 common frames omitted

Additional Info

I'm going to create a pull request to address this.