feat(vector): implement Impression vector upsert and sync

This commit is contained in:
2026-05-28 22:41:41 +08:00
parent 9de46f3589
commit 23a1b7093e
2 changed files with 46 additions and 15 deletions

View File

@@ -138,7 +138,7 @@ class Entity @JvmOverloads constructor(
val relationObject = relationValue as? JSONObject ?: return@forEach val relationObject = relationValue as? JSONObject ?: return@forEach
val relationMap = mutableMapOf<String, Double>() val relationMap = mutableMapOf<String, Double>()
relationObject.forEach { (relation, strengthValue) -> relationObject.forEach { (relation, strengthValue) ->
numberValue(strengthValue)?.let { relationMap[relation] = it } doubleValue(strengthValue)?.let { relationMap[relation] = it }
} }
if (relationMap.isNotEmpty()) { if (relationMap.isNotEmpty()) {
relations[target] = relationMap relations[target] = relationMap
@@ -192,7 +192,7 @@ class Entity @JvmOverloads constructor(
state.forEach { (key, value) -> state.forEach { (key, value) ->
val data = when (value) { val data = when (value) {
is JSONObject -> loadIndexableData(value) is JSONObject -> loadIndexableData(value)
else -> IndexableData(numberValue(value) ?: return@forEach) else -> IndexableData(doubleValue(value) ?: return@forEach)
} }
loaded[key] = data loaded[key] = data
} }
@@ -203,9 +203,9 @@ class Entity @JvmOverloads constructor(
val data = IndexableData(state.getDouble("confidence") ?: 1.0) val data = IndexableData(state.getDouble("confidence") ?: 1.0)
state.getJSONObject("vectors")?.forEach { (embeddingModel, vectorValue) -> state.getJSONObject("vectors")?.forEach { (embeddingModel, vectorValue) ->
val vectorArray = vectorValue as? JSONArray ?: return@forEach val vectorArray = vectorValue as? JSONArray ?: return@forEach
val vector = DoubleArray(vectorArray.size) val vector = FloatArray(vectorArray.size)
for (index in 0 until vectorArray.size) { for (index in vectorArray.indices) {
vector[index] = numberValue(vectorArray[index]) ?: return@forEach vector[index] = floatValue(vectorArray[index]) ?: return@forEach
} }
data.updateVector(embeddingModel, vector) data.updateVector(embeddingModel, vector)
} }
@@ -220,29 +220,35 @@ class Entity @JvmOverloads constructor(
) )
} }
private fun numberValue(value: Any?): Double? = when (value) { private fun doubleValue(value: Any?): Double? = when (value) {
is Number -> value.toDouble() is Number -> value.toDouble()
is String -> value.toDoubleOrNull() is String -> value.toDoubleOrNull()
else -> null else -> null
} }
private fun floatValue(value: Any?): Float? = when (value) {
is Number -> value.toFloat()
is String -> value.toFloatOrNull()
else -> null
}
data class IndexableData( data class IndexableData(
var confidence: Double var confidence: Double
) { ) {
private val vectors: ConcurrentHashMap<String, DoubleArray> = ConcurrentHashMap() private val vectors: ConcurrentHashMap<String, FloatArray> = ConcurrentHashMap()
fun updateVector( fun updateVector(
embeddingModel: String, embeddingModel: String,
vector: DoubleArray vector: FloatArray
) { ) {
vectors[embeddingModel] = vector.copyOf() vectors[embeddingModel] = vector.copyOf()
} }
fun getVector(embeddingModel: String): DoubleArray? { fun getVector(embeddingModel: String): FloatArray? {
return vectors[embeddingModel]?.copyOf() return vectors[embeddingModel]?.copyOf()
} }
fun snapshotVectors(): Map<String, DoubleArray> { fun snapshotVectors(): Map<String, FloatArray> {
return vectors.mapValues { (_, vector) -> vector.copyOf() } return vectors.mapValues { (_, vector) -> vector.copyOf() }
} }
} }
@@ -261,6 +267,6 @@ class Entity @JvmOverloads constructor(
data class ImpressionView( data class ImpressionView(
val impression: String, val impression: String,
val confidence: Double, val confidence: Double,
val vector: DoubleArray? val vector: FloatArray?
) )
} }

View File

@@ -1,13 +1,38 @@
package work.slhaf.partner.core.cognition.impression; package work.slhaf.partner.core.cognition.impression;
import work.slhaf.partner.common.vector.VectorClient;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class ImpressionVectorIndex { public class ImpressionVectorIndex {
public void sync(Entity entity){ private final Executor executor = Executors.newFixedThreadPool(2, r -> {
// TODO sync entity impressions/features with vector index. Thread thread = new Thread(r, "impression-vector-index");
thread.setDaemon(true);
return thread;
});
public void sync(Entity entity) {
if (!VectorClient.status){
return;
}
entity.snapshotFeatures().forEach(this::upsert);
entity.snapshotImpressions().forEach(this::upsert);
} }
public void upsert(String content, Entity.IndexableData indexableData){ public void upsert(String text, Entity.IndexableData indexableData){
// TODO update vector for content when embedding/vector client boundary is finalized. if (VectorClient.status){
return;
}
String modelId = VectorClient.VECTOR_MODEL_ID;
if (indexableData.getVector(modelId) != null) {
return;
}
executor.execute(() -> {
float[] vector = VectorClient.INSTANCE.compute(text);
indexableData.updateVector(modelId,vector);
});
} }
} }