Skip to content

Commit 0ec5d33

Browse files
authored
feat: Add support for enhanced response logging via HttpFormatter (#390)
# Improve response logging via `HttpFormatter` - Colorize JSON string response logs, where possible (näive approach)
1 parent a1aa1f5 commit 0ec5d33

File tree

23 files changed

+223
-43
lines changed

23 files changed

+223
-43
lines changed

ai-mocks-a2a/src/commonMain/kotlin/me/kpavlov/aimocks/a2a/AgentCardBuildingStep.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.a2a
22

3+
import io.ktor.http.ContentType
34
import me.kpavlov.aimocks.core.AbstractBuildingStep
45
import me.kpavlov.mokksy.BuildingStep
56
import me.kpavlov.mokksy.MokksyServer
@@ -13,6 +14,7 @@ public class AgentCardBuildingStep(
1314
val responseDefinition = this.build()
1415
val responseSpecification = AgentCardResponseSpecification(responseDefinition)
1516
block.invoke(responseSpecification)
17+
contentType = ContentType.Application.Json
1618
body = requireNotNull(responseSpecification.card) { "Card must be defined" }
1719
}
1820
}

ai-mocks-anthropic/src/commonMain/kotlin/me/kpavlov/aimocks/anthropic/AnthropicBuildingStep.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.anthropic
22

3+
import io.ktor.http.ContentType
34
import io.ktor.sse.TypedServerSentEvent
45
import io.ktor.utils.io.InternalAPI
56
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,7 +41,7 @@ public class AnthropicBuildingStep(
4041
val stopReason = chatResponseSpecification.stopReason
4142
val completionTokens = LongRange(1, 10).random()
4243
delay = chatResponseSpecification.delay
43-
44+
contentType = ContentType.Application.Json
4445
headers += "x-request-id" to randomIdString("req_")
4546
body =
4647
Message(

ai-mocks-gemini/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ kotlin {
1717
commonMain {
1818
dependencies {
1919
api(libs.ktor.serialization.kotlinx.json)
20+
api(libs.ktor.sse)
2021
api(project(":ai-mocks-core"))
2122
api(project.dependencies.platform(libs.ktor.bom))
2223
}

ai-mocks-gemini/src/commonMain/kotlin/me/kpavlov/aimocks/gemini/content/GeminiContentBuildingStep.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.gemini.content
22

3+
import io.ktor.http.ContentType
34
import me.kpavlov.aimocks.core.AbstractBuildingStep
45
import me.kpavlov.aimocks.gemini.GenerateContentRequest
56
import me.kpavlov.mokksy.BuildingStep
@@ -35,7 +36,7 @@ public class GeminiContentBuildingStep(
3536
block.invoke(chatResponseSpecification)
3637
val assistantContent = chatResponseSpecification.content
3738
delay = chatResponseSpecification.delay
38-
39+
contentType = ContentType.Application.Json
3940
body =
4041
generateContentResponse(
4142
assistantContent = assistantContent,

ai-mocks-gemini/src/commonMain/kotlin/me/kpavlov/aimocks/gemini/content/GeminiStreamingContentBuildingStep.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package me.kpavlov.aimocks.gemini.content
22

3+
import io.ktor.sse.TypedServerSentEvent
4+
import io.ktor.utils.io.InternalAPI
35
import kotlinx.coroutines.flow.Flow
46
import kotlinx.coroutines.flow.asFlow
57
import kotlinx.coroutines.flow.map
@@ -84,22 +86,22 @@ public class GeminiStreamingContentBuildingStep(
8486
}
8587
}
8688

89+
@OptIn(InternalAPI::class)
8790
private fun encodeChunk(
8891
chunk: GenerateContentResponse,
8992
sse: Boolean,
9093
lastChunk: Boolean = false,
9194
): String {
92-
val json =
93-
Json.encodeToString(
94-
value = chunk,
95-
serializer = GenerateContentResponse.serializer(),
96-
)
9795
return if (sse) {
98-
"data: $json\r\n\r\n"
96+
TypedServerSentEvent(
97+
data = chunk,
98+
).toString {
99+
Json.encodeToString(it)
100+
}
99101
} else if (lastChunk) {
100-
json
102+
Json.encodeToString(value = chunk)
101103
} else {
102-
"$json,\r\n"
104+
"${Json.encodeToString(value = chunk)},\r\n"
103105
}
104106
}
105107

ai-mocks-ollama/src/commonMain/kotlin/me/kpavlov/aimocks/ollama/chat/OllamaChatBuildingStep.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.ollama.chat
22

3+
import io.ktor.http.ContentType
34
import kotlinx.coroutines.ExperimentalCoroutinesApi
45
import kotlinx.coroutines.flow.Flow
56
import kotlinx.coroutines.flow.asFlow
@@ -55,6 +56,7 @@ public class OllamaChatBuildingStep(
5556
)
5657
block.invoke(chatResponseSpecification)
5758
delay = chatResponseSpecification.delay
59+
contentType = ContentType.Application.Json
5860

5961
val promptEvalCount = nextInt(1, 200)
6062
val evalCount = nextInt(1, 500)

ai-mocks-ollama/src/commonMain/kotlin/me/kpavlov/aimocks/ollama/embed/OllamaEmbedBuildingStep.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.ollama.embed
22

3+
import io.ktor.http.ContentType
34
import kotlinx.datetime.Clock
45
import me.kpavlov.aimocks.core.AbstractBuildingStep
56
import me.kpavlov.aimocks.core.EmbeddingUtils
@@ -51,6 +52,7 @@ public class OllamaEmbedBuildingStep(
5152
?: request.input.map { EmbeddingUtils.generateEmbedding(it) }
5253
val modelName = embedResponseSpecification.model ?: request.model
5354
delay = embedResponseSpecification.delay
55+
contentType = ContentType.Application.Json
5456

5557
@Suppress("MagicNumber")
5658
val promptEvalCount = nextInt(1, 200)

ai-mocks-ollama/src/commonMain/kotlin/me/kpavlov/aimocks/ollama/generate/OllamaGenerateBuildingStep.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.ollama.generate
22

3+
import io.ktor.http.ContentType
34
import kotlinx.coroutines.ExperimentalCoroutinesApi
45
import kotlinx.coroutines.flow.Flow
56
import kotlinx.coroutines.flow.asFlow
@@ -55,6 +56,7 @@ public class OllamaGenerateBuildingStep(
5556
val responseContent = generateResponseSpecification.responseContent
5657
val doneReason = generateResponseSpecification.doneReason
5758
delay = generateResponseSpecification.delay
59+
contentType = ContentType.Application.Json
5860

5961
val promptEvalCount = nextInt(1, 200)
6062
val evalCount = nextInt(1, 500)

ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/completions/OpenaiChatCompletionsBuildingStep.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.openai.completions
22

3+
import io.ktor.http.ContentType
34
import kotlinx.coroutines.ExperimentalCoroutinesApi
45
import kotlinx.coroutines.flow.Flow
56
import kotlinx.coroutines.flow.asFlow
@@ -63,6 +64,7 @@ public class OpenaiChatCompletionsBuildingStep(
6364
val assistantContent = chatResponseSpecification.assistantContent
6465
val finishReason = chatResponseSpecification.finishReason
6566
delay = chatResponseSpecification.delay
67+
contentType = ContentType.Application.Json
6668

6769
val promptTokens = nextInt(1, 200)
6870
val completionTokens = nextInt(1, request.maxCompletionTokens ?: 500)

ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/embeddings/OpenaiEmbedBuildingStep.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.kpavlov.aimocks.openai.embeddings
22

3+
import io.ktor.http.ContentType
34
import me.kpavlov.aimocks.core.AbstractBuildingStep
45
import me.kpavlov.aimocks.core.EmbeddingUtils
56
import me.kpavlov.aimocks.openai.model.embeddings.CreateEmbeddingsRequest
@@ -55,6 +56,7 @@ public class OpenaiEmbedBuildingStep(
5556
responseSpecification.embeddings
5657
?: request.input.map { EmbeddingUtils.generateEmbedding(it) }
5758
delay = responseSpecification.delay
59+
contentType = ContentType.Application.Json
5860

5961
val promptTokens = nextInt(1, 100)
6062
val totalTokens = nextInt(promptTokens, promptTokens + 500)

0 commit comments

Comments
 (0)