fix(router): make the real embedder + TurboVec L3 actually work end-to-end
- LlamaCppEmbedder: parse llama.cpp --pooling mean nested response [[...]] (was silently failing every embed with 'unexpected response shape') - qa-stack: embedder -b/-ub 8192 so docs >512 tokens don't 500; fix default path - TurboVecL3MemoryStore: send init handshake on startup (was 'Index not initialized' on every add/query) - turbovec_sidecar.py: rewrite add/search/save/load against the real turbovec API (batched ndarray, L2-normalize so IP=cosine, prepare() before search, classmethod load). The sidecar was scaffolding never run against the real lib.
This commit is contained in:
+9
@@ -116,6 +116,15 @@ class LlamaCppEmbedder(
|
||||
return arrayResult
|
||||
}
|
||||
|
||||
// llama.cpp --pooling mean: [{"embedding": [[...]]}] (nested list-of-one-vector).
|
||||
val nestedResult = runCatching {
|
||||
json.decodeFromString<List<EmbeddingNestedArrayResponse>>(responseBody)
|
||||
.firstOrNull()?.embedding?.firstOrNull().orEmpty()
|
||||
}.getOrNull()
|
||||
if (nestedResult != null && nestedResult.isNotEmpty()) {
|
||||
return nestedResult
|
||||
}
|
||||
|
||||
throw IllegalStateException(
|
||||
"Unexpected embedding response shape from $url. " +
|
||||
"Response body (first 200 chars): ${responseBody.take(200)}"
|
||||
|
||||
+7
@@ -19,6 +19,13 @@ data class EmbeddingArrayResponse(
|
||||
val embedding: List<Float>,
|
||||
)
|
||||
|
||||
// llama.cpp /embedding with --pooling mean returns a list-of-one-vector per item:
|
||||
// [{"index":0,"embedding":[[floats...]]}]. embedding is nested, not flat.
|
||||
@Serializable
|
||||
data class EmbeddingNestedArrayResponse(
|
||||
val embedding: List<List<Float>>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class OpenAiEmbeddingData(
|
||||
val embedding: List<Float>,
|
||||
|
||||
+22
@@ -79,6 +79,28 @@ class LlamaCppEmbedderTest {
|
||||
assert(kotlin.math.abs(result[2] - 0.7f) < 0.0001f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `embed parses llama-cpp nested pooling-mean array format`(): Unit = runBlocking {
|
||||
// llama.cpp /embedding with --pooling mean: [{"index":0,"embedding":[[...]]}]
|
||||
val mockEngine = MockEngine {
|
||||
respond(
|
||||
content = """[{"index":0,"embedding":[[0.5,0.6,0.7]]}]""",
|
||||
headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString())
|
||||
)
|
||||
}
|
||||
val embedder = LlamaCppEmbedder(
|
||||
url = "http://localhost:11000",
|
||||
dimension = 3,
|
||||
httpClient = HttpClient(mockEngine),
|
||||
)
|
||||
|
||||
val result = embedder.embed("test text")
|
||||
|
||||
assert(result.size == 3)
|
||||
assert(kotlin.math.abs(result[0] - 0.5f) < 0.0001f)
|
||||
assert(kotlin.math.abs(result[2] - 0.7f) < 0.0001f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `embed throws on dimension mismatch`(): Unit = runBlocking {
|
||||
val mockEngine = MockEngine { request ->
|
||||
|
||||
Reference in New Issue
Block a user