Graph embeddings works

This commit is contained in:
Cyber MacGeddon 2025-04-19 12:06:40 +01:00
parent e6b1bb4919
commit 3507bc2c6e
5 changed files with 137 additions and 76 deletions

View file

@ -13,6 +13,6 @@ from . producer_spec import ProducerSpec
from . subscriber_spec import SubscriberSpec from . subscriber_spec import SubscriberSpec
from . request_response_spec import RequestResponseSpec from . request_response_spec import RequestResponseSpec
from . llm_service import LlmService, LlmResult from . llm_service import LlmService, LlmResult
from . embeddings_service import EmbeddingsService

View file

@ -0,0 +1,90 @@
"""
Embeddings resolution base class
"""
import time
from prometheus_client import Histogram
from .. schema import EmbeddingsRequest, EmbeddingsResponse, Error
from .. exceptions import TooManyRequests
from .. base import FlowProcessor, ConsumerSpec, ProducerSpec
default_ident = "embeddings"
class EmbeddingsService(FlowProcessor):
def __init__(self, **params):
id = params.get("id")
super(EmbeddingsService, self).__init__(**params | { "id": id })
self.register_specification(
ConsumerSpec(
name = "request",
schema = EmbeddingsRequest,
handler = self.on_request
)
)
self.register_specification(
ProducerSpec(
name = "response",
schema = EmbeddingsResponse
)
)
async def on_request(self, msg, consumer, flow):
try:
request = msg.value()
# Sender-produced ID
id = msg.properties()["id"]
print("Handling request", id, "...", flush=True)
vectors = await self.on_embeddings(request.text)
await flow("response").send(
EmbeddingsResponse(
error = None,
vectors = vectors,
),
properties={"id": id}
)
print("Handled.", flush=True)
except TooManyRequests as e:
raise e
except Exception as e:
# Apart from rate limits, treat all exceptions as unrecoverable
print(f"Exception: {e}", flush=True)
print("Send error response...", flush=True)
await flow.producer["response"].send(
EmbeddingsResponse(
error=Error(
type = "embeddings-error",
message = str(e),
),
vectors=None,
),
properties={"id": id}
)
@staticmethod
def add_args(parser):
FlowProcessor.add_args(parser)

View file

@ -73,7 +73,7 @@ class LlmService(FlowProcessor):
request.system, request.prompt request.system, request.prompt
) )
await flow.producer["response"].send( await flow("response").send(
TextCompletionResponse( TextCompletionResponse(
error=None, error=None,
response=response.text, response=response.text,

View file

@ -1,55 +1,42 @@
""" """
Embeddings service, applies an embeddings model selected from HuggingFace. Embeddings service, applies an embeddings model using fastembed
Input is text, output is embeddings vector. Input is text, output is embeddings vector.
""" """
from ... schema import EmbeddingsRequest, EmbeddingsResponse from ... base import EmbeddingsService
from ... base import RequestResponseService
from fastembed import TextEmbedding from fastembed import TextEmbedding
import os
module = "embeddings" default_ident = "embeddings"
default_subscriber = module
default_model="sentence-transformers/all-MiniLM-L6-v2" default_model="sentence-transformers/all-MiniLM-L6-v2"
class Processor(RequestResponseService): class Processor(EmbeddingsService):
def __init__(self, **params): def __init__(self, **params):
model = params.get("model", default_model) model = params.get("model", default_model)
super(Processor, self).__init__( super(Processor, self).__init__(
**params | { **params | { "model": model }
"model": model,
"request_schema": EmbeddingsRequest,
"response_schema": EmbeddingsResponse,
}
) )
print("Get model...", flush=True)
self.embeddings = TextEmbedding(model_name = model) self.embeddings = TextEmbedding(model_name = model)
async def on_request(self, request, consumer, flow): async def on_embeddings(self, text):
text = request.text
vecs = self.embeddings.embed([text]) vecs = self.embeddings.embed([text])
vecs = [ return [
v.tolist() v.tolist()
for v in vecs for v in vecs
] ]
return EmbeddingsResponse(
vectors=list(vecs),
error=None,
)
@staticmethod @staticmethod
def add_args(parser): def add_args(parser):
RequestResponseService.add_args(parser, default_subscriber) EmbeddingsService.add_args(parser)
parser.add_argument( parser.add_argument(
'-m', '--model', '-m', '--model',
@ -59,5 +46,6 @@ class Processor(RequestResponseService):
def run(): def run():
Processor.launch(module, __doc__) Processor.launch(default_ident, __doc__)

View file

@ -6,58 +6,50 @@ Output is entity plus embedding.
""" """
from ... schema import EntityContexts, EntityEmbeddings, GraphEmbeddings from ... schema import EntityContexts, EntityEmbeddings, GraphEmbeddings
from ... schema import entity_contexts_ingest_queue from ... schema import EmbeddingsRequest, EmbeddingsResponse
from ... schema import graph_embeddings_store_queue
from ... schema import embeddings_request_queue, embeddings_response_queue
from ... clients.embeddings_client import EmbeddingsClient
from ... log_level import LogLevel
from ... base import FlowProcessor
module = "graph-embeddings" from ... base import FlowProcessor, RequestResponseSpec, ConsumerSpec
from ... base import ProducerSpec
default_subscriber = module default_ident = "graph-embeddings"
class Processor(FlowProcessor): class Processor(FlowProcessor):
def __init__(self, **params): def __init__(self, **params):
"input_schema": EntityContexts,
"output_schema": GraphEmbeddings,
id = params.get("id") id = params.get("id")
subscriber = params.get("subscriber", default_subscriber)
super(Processor, self).__init__( super(Processor, self).__init__(
**params | { **params | {
"id": id, "id": id,
"subscriber": subscriber,
} }
) )
self.register_consumer( self.register_specification(
name = "input", ConsumerSpec(
schema = EntityContexts, name = "input",
handler = self.on_message, schema = EntityContexts,
handler = self.on_message,
)
) )
self.register_producer( self.register_specification(
name = "output", RequestResponseSpec(
schema = GraphEmbeddings, request_name = "embeddings-request",
request_schema = EmbeddingsRequest,
response_name = "embeddings-response",
response_schema = EmbeddingsResponse,
)
) )
# self.embeddings = EmbeddingsClient( self.register_specification(
# pulsar_host=self.pulsar_host, ProducerSpec(
# input_queue=emb_request_queue, name = "output",
# output_queue=emb_response_queue, schema = GraphEmbeddings
# subscriber=module + "-emb", )
# ) )
async def handle(self, msg): async def on_message(self, msg, consumer, flow):
v = msg.value() v = msg.value()
print(f"Indexing {v.metadata.id}...", flush=True) print(f"Indexing {v.metadata.id}...", flush=True)
@ -68,7 +60,13 @@ class Processor(FlowProcessor):
for entity in v.entities: for entity in v.entities:
vectors = self.embeddings.request(entity.context) resp = await flow("embeddings-request").request(
EmbeddingsRequest(
text = entity.context
)
)
vectors = resp.vectors
entities.append( entities.append(
EntityEmbeddings( EntityEmbeddings(
@ -82,7 +80,7 @@ class Processor(FlowProcessor):
entities=entities, entities=entities,
) )
await self.send(r) await flow("output").send(r)
except Exception as e: except Exception as e:
print("Exception:", e, flush=True) print("Exception:", e, flush=True)
@ -95,24 +93,9 @@ class Processor(FlowProcessor):
@staticmethod @staticmethod
def add_args(parser): def add_args(parser):
ConsumerProducer.add_args( FlowProcessor.add_args(parser)
parser, default_input_queue, default_subscriber,
default_output_queue,
)
parser.add_argument(
'--embeddings-request-queue',
default=embeddings_request_queue,
help=f'Embeddings request queue (default: {embeddings_request_queue})',
)
parser.add_argument(
'--embeddings-response-queue',
default=embeddings_response_queue,
help=f'Embeddings request queue (default: {embeddings_response_queue})',
)
def run(): def run():
Processor.launch(module, __doc__) Processor.launch(default_ident, __doc__)