fix(gateway): restore safe search fallback
Fall back to guarded Chat Completions answers when DeepSeek web search fails so grounded current-information hints do not surface generic provider errors.
This commit is contained in:
+5
-4
@@ -9,6 +9,7 @@ import com.osglab.account.features.gateway.models.ProviderOutput
|
||||
import com.osglab.account.features.gateway.models.ProviderUsage
|
||||
import com.osglab.account.features.gateway.models.TextProviderRequest
|
||||
import com.osglab.account.features.gateway.models.UsageMeter
|
||||
import com.osglab.account.features.gateway.providers.ProviderUpstreamException
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.bearerAuth
|
||||
@@ -72,16 +73,16 @@ internal class DeepSeekSearchFallbackClient(
|
||||
} catch (failure: CancellationException) {
|
||||
throw failure
|
||||
} catch (failure: Exception) {
|
||||
val upstreamStatus = (failure as? ProviderUpstreamException)?.upstreamStatus
|
||||
LOG.warn(
|
||||
"DeepSeek search path failed requestId={} taskKind={} searchMode={} failureType={}",
|
||||
"DeepSeek search path failed requestId={} taskKind={} searchMode={} " +
|
||||
"failureType={} upstreamStatus={} fallback=chat_completions",
|
||||
request.requestId,
|
||||
request.executionPolicy.taskKind.name,
|
||||
request.executionPolicy.webSearch.name,
|
||||
failure::class.simpleName ?: "Exception",
|
||||
upstreamStatus ?: "unknown",
|
||||
)
|
||||
if (request.executionPolicy.webSearch == GatewayWebSearchMode.REQUIRED) {
|
||||
throw failure
|
||||
}
|
||||
return fallback.complete(
|
||||
request.copy(
|
||||
executionPolicy = request.executionPolicy.copy(
|
||||
|
||||
+31
-16
@@ -291,16 +291,28 @@ class DeepSeekClientTest : StringSpec({
|
||||
}
|
||||
}
|
||||
|
||||
"does not return an offline answer when current information requires search" {
|
||||
"falls back to a guarded answer when required search fails" {
|
||||
val paths = mutableListOf<String>()
|
||||
val requestBodies = mutableListOf<String>()
|
||||
val client = HttpClient(
|
||||
MockEngine { request ->
|
||||
paths += request.url.encodedPath
|
||||
respond(
|
||||
content = """{"error":{"message":"search unavailable"}}""",
|
||||
status = HttpStatusCode.ServiceUnavailable,
|
||||
headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString()),
|
||||
)
|
||||
requestBodies += request.body.toByteArray().decodeToString()
|
||||
if (request.url.encodedPath.endsWith("/responses")) {
|
||||
respond(
|
||||
content = """{"error":{"message":"search unavailable"}}""",
|
||||
status = HttpStatusCode.ServiceUnavailable,
|
||||
headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString()),
|
||||
)
|
||||
} else {
|
||||
respond(
|
||||
content =
|
||||
"""{"choices":[{"message":{"content":"无法核实实时信息"}}],""" +
|
||||
""""usage":{"prompt_tokens":9,"completion_tokens":4,"total_tokens":13}}""",
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString()),
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -308,17 +320,20 @@ class DeepSeekClientTest : StringSpec({
|
||||
}
|
||||
}
|
||||
try {
|
||||
shouldThrow<DeepSeekProviderException> {
|
||||
DeepSeekProvider(client, CONFIG).execute(
|
||||
request(
|
||||
taskKind = GatewayTaskKind.CURRENT_INFORMATION_QUESTION,
|
||||
webSearch = GatewayWebSearchMode.REQUIRED,
|
||||
),
|
||||
DISCARD_OUTPUT,
|
||||
)
|
||||
}
|
||||
val usage = DeepSeekProvider(client, CONFIG).execute(
|
||||
request(
|
||||
taskKind = GatewayTaskKind.CURRENT_INFORMATION_QUESTION,
|
||||
webSearch = GatewayWebSearchMode.REQUIRED,
|
||||
),
|
||||
DISCARD_OUTPUT,
|
||||
)
|
||||
|
||||
paths shouldBe listOf("/v1/responses")
|
||||
paths shouldBe listOf("/v1/responses", "/v1/chat/completions")
|
||||
val fallbackSystem = Json.parseToJsonElement(requestBodies.last()).jsonObject
|
||||
.getValue("messages").jsonArray.first().jsonObject
|
||||
.getValue("content").jsonPrimitive.content
|
||||
fallbackSystem shouldContain "could not be verified"
|
||||
usage.units shouldBe 13
|
||||
} finally {
|
||||
client.close()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user