agent-brain-rag 1.2.0__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_brain_rag-1.2.0.dist-info → agent_brain_rag-3.0.0.dist-info}/METADATA +55 -18
- agent_brain_rag-3.0.0.dist-info/RECORD +56 -0
- {agent_brain_rag-1.2.0.dist-info → agent_brain_rag-3.0.0.dist-info}/WHEEL +1 -1
- {agent_brain_rag-1.2.0.dist-info → agent_brain_rag-3.0.0.dist-info}/entry_points.txt +0 -1
- agent_brain_server/__init__.py +1 -1
- agent_brain_server/api/main.py +146 -45
- agent_brain_server/api/routers/__init__.py +2 -0
- agent_brain_server/api/routers/health.py +85 -21
- agent_brain_server/api/routers/index.py +108 -36
- agent_brain_server/api/routers/jobs.py +111 -0
- agent_brain_server/config/provider_config.py +352 -0
- agent_brain_server/config/settings.py +22 -5
- agent_brain_server/indexing/__init__.py +21 -0
- agent_brain_server/indexing/bm25_index.py +15 -2
- agent_brain_server/indexing/document_loader.py +45 -4
- agent_brain_server/indexing/embedding.py +86 -135
- agent_brain_server/indexing/graph_extractors.py +582 -0
- agent_brain_server/indexing/graph_index.py +536 -0
- agent_brain_server/job_queue/__init__.py +11 -0
- agent_brain_server/job_queue/job_service.py +317 -0
- agent_brain_server/job_queue/job_store.py +427 -0
- agent_brain_server/job_queue/job_worker.py +434 -0
- agent_brain_server/locking.py +101 -8
- agent_brain_server/models/__init__.py +28 -0
- agent_brain_server/models/graph.py +253 -0
- agent_brain_server/models/health.py +30 -3
- agent_brain_server/models/job.py +289 -0
- agent_brain_server/models/query.py +16 -3
- agent_brain_server/project_root.py +1 -1
- agent_brain_server/providers/__init__.py +64 -0
- agent_brain_server/providers/base.py +251 -0
- agent_brain_server/providers/embedding/__init__.py +23 -0
- agent_brain_server/providers/embedding/cohere.py +163 -0
- agent_brain_server/providers/embedding/ollama.py +150 -0
- agent_brain_server/providers/embedding/openai.py +118 -0
- agent_brain_server/providers/exceptions.py +95 -0
- agent_brain_server/providers/factory.py +157 -0
- agent_brain_server/providers/summarization/__init__.py +41 -0
- agent_brain_server/providers/summarization/anthropic.py +87 -0
- agent_brain_server/providers/summarization/gemini.py +96 -0
- agent_brain_server/providers/summarization/grok.py +95 -0
- agent_brain_server/providers/summarization/ollama.py +114 -0
- agent_brain_server/providers/summarization/openai.py +87 -0
- agent_brain_server/runtime.py +2 -2
- agent_brain_server/services/indexing_service.py +39 -0
- agent_brain_server/services/query_service.py +203 -0
- agent_brain_server/storage/__init__.py +18 -2
- agent_brain_server/storage/graph_store.py +519 -0
- agent_brain_server/storage/vector_store.py +35 -0
- agent_brain_server/storage_paths.py +5 -3
- agent_brain_rag-1.2.0.dist-info/RECORD +0 -31
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
"""Embedding generation using
|
|
1
|
+
"""Embedding generation using pluggable providers.
|
|
2
|
+
|
|
3
|
+
This module provides embedding and summarization functionality using
|
|
4
|
+
the configurable provider system. Providers are selected based on
|
|
5
|
+
config.yaml or environment defaults.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
import logging
|
|
9
|
+
import re
|
|
4
10
|
from collections.abc import Awaitable, Callable
|
|
5
|
-
from typing import Optional
|
|
11
|
+
from typing import TYPE_CHECKING, Optional
|
|
6
12
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
13
|
+
from agent_brain_server.config.provider_config import load_provider_settings
|
|
14
|
+
from agent_brain_server.providers.factory import ProviderRegistry
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from agent_brain_server.providers.base import (
|
|
18
|
+
EmbeddingProvider,
|
|
19
|
+
SummarizationProvider,
|
|
20
|
+
)
|
|
11
21
|
|
|
12
22
|
from .chunking import TextChunk
|
|
13
23
|
|
|
@@ -15,54 +25,68 @@ logger = logging.getLogger(__name__)
|
|
|
15
25
|
|
|
16
26
|
|
|
17
27
|
class EmbeddingGenerator:
|
|
18
|
-
"""
|
|
19
|
-
Generates embeddings using OpenAI's embedding models.
|
|
28
|
+
"""Generates embeddings and summaries using pluggable providers.
|
|
20
29
|
|
|
21
30
|
Supports batch processing with configurable batch sizes
|
|
22
|
-
and automatic
|
|
31
|
+
and automatic provider selection based on configuration.
|
|
23
32
|
"""
|
|
24
33
|
|
|
25
34
|
def __init__(
|
|
26
35
|
self,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
batch_size: Optional[int] = None,
|
|
36
|
+
embedding_provider: Optional["EmbeddingProvider"] = None,
|
|
37
|
+
summarization_provider: Optional["SummarizationProvider"] = None,
|
|
30
38
|
):
|
|
31
|
-
"""
|
|
32
|
-
Initialize the embedding generator.
|
|
39
|
+
"""Initialize the embedding generator.
|
|
33
40
|
|
|
34
41
|
Args:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
embedding_provider: Optional embedding provider. If not provided,
|
|
43
|
+
creates one from configuration.
|
|
44
|
+
summarization_provider: Optional summarization provider. If not
|
|
45
|
+
provided, creates one from configuration.
|
|
46
|
+
"""
|
|
47
|
+
# Load configuration
|
|
48
|
+
settings = load_provider_settings()
|
|
49
|
+
|
|
50
|
+
# Initialize providers from config or use provided ones
|
|
51
|
+
if embedding_provider is not None:
|
|
52
|
+
self._embedding_provider = embedding_provider
|
|
53
|
+
else:
|
|
54
|
+
self._embedding_provider = ProviderRegistry.get_embedding_provider(
|
|
55
|
+
settings.embedding
|
|
56
|
+
)
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
if summarization_provider is not None:
|
|
59
|
+
self._summarization_provider = summarization_provider
|
|
60
|
+
else:
|
|
61
|
+
self._summarization_provider = ProviderRegistry.get_summarization_provider(
|
|
62
|
+
settings.summarization
|
|
63
|
+
)
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
logger.info(
|
|
66
|
+
f"EmbeddingGenerator initialized with "
|
|
67
|
+
f"{self._embedding_provider.provider_name} embeddings "
|
|
68
|
+
f"({self._embedding_provider.model_name}) and "
|
|
69
|
+
f"{self._summarization_provider.provider_name} summarization "
|
|
70
|
+
f"({self._summarization_provider.model_name})"
|
|
50
71
|
)
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
@property
|
|
74
|
+
def model(self) -> str:
|
|
75
|
+
"""Get the embedding model name."""
|
|
76
|
+
return self._embedding_provider.model_name
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def embedding_provider(self) -> "EmbeddingProvider":
|
|
80
|
+
"""Get the embedding provider."""
|
|
81
|
+
return self._embedding_provider
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def summarization_provider(self) -> "SummarizationProvider":
|
|
85
|
+
"""Get the summarization provider."""
|
|
86
|
+
return self._summarization_provider
|
|
62
87
|
|
|
63
88
|
async def embed_text(self, text: str) -> list[float]:
|
|
64
|
-
"""
|
|
65
|
-
Generate embedding for a single text.
|
|
89
|
+
"""Generate embedding for a single text.
|
|
66
90
|
|
|
67
91
|
Args:
|
|
68
92
|
text: Text to embed.
|
|
@@ -70,19 +94,14 @@ class EmbeddingGenerator:
|
|
|
70
94
|
Returns:
|
|
71
95
|
Embedding vector as list of floats.
|
|
72
96
|
"""
|
|
73
|
-
|
|
74
|
-
model=self.model,
|
|
75
|
-
input=text,
|
|
76
|
-
)
|
|
77
|
-
return response.data[0].embedding
|
|
97
|
+
return await self._embedding_provider.embed_text(text)
|
|
78
98
|
|
|
79
99
|
async def embed_texts(
|
|
80
100
|
self,
|
|
81
101
|
texts: list[str],
|
|
82
102
|
progress_callback: Optional[Callable[[int, int], Awaitable[None]]] = None,
|
|
83
103
|
) -> list[list[float]]:
|
|
84
|
-
"""
|
|
85
|
-
Generate embeddings for multiple texts.
|
|
104
|
+
"""Generate embeddings for multiple texts.
|
|
86
105
|
|
|
87
106
|
Args:
|
|
88
107
|
texts: List of texts to embed.
|
|
@@ -91,49 +110,14 @@ class EmbeddingGenerator:
|
|
|
91
110
|
Returns:
|
|
92
111
|
List of embedding vectors.
|
|
93
112
|
"""
|
|
94
|
-
|
|
95
|
-
return []
|
|
96
|
-
|
|
97
|
-
all_embeddings: list[list[float]] = []
|
|
98
|
-
|
|
99
|
-
# Process in batches to respect API limits
|
|
100
|
-
for i in range(0, len(texts), self.batch_size):
|
|
101
|
-
batch = texts[i : i + self.batch_size]
|
|
102
|
-
|
|
103
|
-
try:
|
|
104
|
-
response = await self.client.embeddings.create(
|
|
105
|
-
model=self.model,
|
|
106
|
-
input=batch,
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
# Extract embeddings in order
|
|
110
|
-
batch_embeddings = [item.embedding for item in response.data]
|
|
111
|
-
all_embeddings.extend(batch_embeddings)
|
|
112
|
-
|
|
113
|
-
if progress_callback:
|
|
114
|
-
await progress_callback(
|
|
115
|
-
min(i + self.batch_size, len(texts)),
|
|
116
|
-
len(texts),
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
logger.debug(
|
|
120
|
-
f"Generated embeddings for batch {i // self.batch_size + 1} "
|
|
121
|
-
f"({len(batch)} texts)"
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
except Exception as e:
|
|
125
|
-
logger.error(f"Failed to generate embeddings for batch: {e}")
|
|
126
|
-
raise
|
|
127
|
-
|
|
128
|
-
return all_embeddings
|
|
113
|
+
return await self._embedding_provider.embed_texts(texts, progress_callback)
|
|
129
114
|
|
|
130
115
|
async def embed_chunks(
|
|
131
116
|
self,
|
|
132
117
|
chunks: list[TextChunk],
|
|
133
118
|
progress_callback: Optional[Callable[[int, int], Awaitable[None]]] = None,
|
|
134
119
|
) -> list[list[float]]:
|
|
135
|
-
"""
|
|
136
|
-
Generate embeddings for a list of text chunks.
|
|
120
|
+
"""Generate embeddings for a list of text chunks.
|
|
137
121
|
|
|
138
122
|
Args:
|
|
139
123
|
chunks: List of TextChunk objects.
|
|
@@ -146,8 +130,7 @@ class EmbeddingGenerator:
|
|
|
146
130
|
return await self.embed_texts(texts, progress_callback)
|
|
147
131
|
|
|
148
132
|
async def embed_query(self, query: str) -> list[float]:
|
|
149
|
-
"""
|
|
150
|
-
Generate embedding for a search query.
|
|
133
|
+
"""Generate embedding for a search query.
|
|
151
134
|
|
|
152
135
|
This is a convenience wrapper around embed_text for queries.
|
|
153
136
|
|
|
@@ -160,41 +143,15 @@ class EmbeddingGenerator:
|
|
|
160
143
|
return await self.embed_text(query)
|
|
161
144
|
|
|
162
145
|
def get_embedding_dimensions(self) -> int:
|
|
163
|
-
"""
|
|
164
|
-
Get the expected embedding dimensions for the current model.
|
|
146
|
+
"""Get the expected embedding dimensions for the current model.
|
|
165
147
|
|
|
166
148
|
Returns:
|
|
167
149
|
Number of dimensions in the embedding vector.
|
|
168
150
|
"""
|
|
169
|
-
|
|
170
|
-
model_dimensions = {
|
|
171
|
-
"text-embedding-3-large": 3072,
|
|
172
|
-
"text-embedding-3-small": 1536,
|
|
173
|
-
"text-embedding-ada-002": 1536,
|
|
174
|
-
}
|
|
175
|
-
return model_dimensions.get(self.model, settings.EMBEDDING_DIMENSIONS)
|
|
176
|
-
|
|
177
|
-
def _get_summary_prompt_template(self) -> str:
|
|
178
|
-
"""
|
|
179
|
-
Get the prompt template for code summarization.
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
Prompt template string.
|
|
183
|
-
"""
|
|
184
|
-
template = (
|
|
185
|
-
"You are an expert software engineer analyzing source code. "
|
|
186
|
-
"Provide a concise 1-2 sentence summary of what this code does. "
|
|
187
|
-
"Focus on the functionality, purpose, and behavior. "
|
|
188
|
-
"Be specific about inputs, outputs, and side effects. "
|
|
189
|
-
"Ignore implementation details and focus on what the code accomplishes.\n\n"
|
|
190
|
-
"Code to summarize:\n{context_str}\n\n"
|
|
191
|
-
"Summary:"
|
|
192
|
-
)
|
|
193
|
-
return template
|
|
151
|
+
return self._embedding_provider.get_dimensions()
|
|
194
152
|
|
|
195
153
|
async def generate_summary(self, code_text: str) -> str:
|
|
196
|
-
"""
|
|
197
|
-
Generate a natural language summary of code using Claude.
|
|
154
|
+
"""Generate a natural language summary of code.
|
|
198
155
|
|
|
199
156
|
Args:
|
|
200
157
|
code_text: The source code to summarize.
|
|
@@ -203,33 +160,23 @@ class EmbeddingGenerator:
|
|
|
203
160
|
Natural language summary of the code's functionality.
|
|
204
161
|
"""
|
|
205
162
|
try:
|
|
206
|
-
|
|
207
|
-
prompt = self.summary_prompt_template.format(context_str=code_text)
|
|
208
|
-
|
|
209
|
-
response = await self.anthropic_client.messages.create(
|
|
210
|
-
model=settings.CLAUDE_MODEL,
|
|
211
|
-
max_tokens=300,
|
|
212
|
-
temperature=0.1, # Low temperature for consistent summaries
|
|
213
|
-
messages=[{"role": "user", "content": prompt}],
|
|
214
|
-
)
|
|
163
|
+
summary = await self._summarization_provider.summarize(code_text)
|
|
215
164
|
|
|
216
|
-
|
|
217
|
-
summary = response.content[0].text # type: ignore
|
|
218
|
-
|
|
219
|
-
if summary and len(summary) > 10: # Ensure we got a meaningful summary
|
|
165
|
+
if summary and len(summary) > 10:
|
|
220
166
|
return summary
|
|
221
167
|
else:
|
|
222
|
-
logger.warning(
|
|
168
|
+
logger.warning(
|
|
169
|
+
f"{self._summarization_provider.provider_name} "
|
|
170
|
+
"returned empty or too short summary"
|
|
171
|
+
)
|
|
223
172
|
return self._extract_fallback_summary(code_text)
|
|
224
173
|
|
|
225
174
|
except Exception as e:
|
|
226
175
|
logger.error(f"Failed to generate code summary: {e}")
|
|
227
|
-
# Fallback: try to extract from docstrings/comments
|
|
228
176
|
return self._extract_fallback_summary(code_text)
|
|
229
177
|
|
|
230
178
|
def _extract_fallback_summary(self, code_text: str) -> str:
|
|
231
|
-
"""
|
|
232
|
-
Extract summary from docstrings or comments as fallback.
|
|
179
|
+
"""Extract summary from docstrings or comments as fallback.
|
|
233
180
|
|
|
234
181
|
Args:
|
|
235
182
|
code_text: Source code to analyze.
|
|
@@ -237,13 +184,11 @@ class EmbeddingGenerator:
|
|
|
237
184
|
Returns:
|
|
238
185
|
Extracted summary or empty string.
|
|
239
186
|
"""
|
|
240
|
-
import re
|
|
241
|
-
|
|
242
187
|
# Try to find Python docstrings
|
|
243
188
|
docstring_match = re.search(r'""".*?"""', code_text, re.DOTALL)
|
|
244
189
|
if docstring_match:
|
|
245
|
-
docstring = docstring_match.group(0)[3:-3]
|
|
246
|
-
if len(docstring) > 10:
|
|
190
|
+
docstring = docstring_match.group(0)[3:-3]
|
|
191
|
+
if len(docstring) > 10:
|
|
247
192
|
return docstring[:200] + "..." if len(docstring) > 200 else docstring
|
|
248
193
|
|
|
249
194
|
# Try to find function/class comments
|
|
@@ -259,7 +204,7 @@ class EmbeddingGenerator:
|
|
|
259
204
|
if first_line.startswith(("#", "//", "/*")):
|
|
260
205
|
return first_line.lstrip("#/*").strip()
|
|
261
206
|
|
|
262
|
-
return ""
|
|
207
|
+
return ""
|
|
263
208
|
|
|
264
209
|
|
|
265
210
|
# Singleton instance
|
|
@@ -272,3 +217,9 @@ def get_embedding_generator() -> EmbeddingGenerator:
|
|
|
272
217
|
if _embedding_generator is None:
|
|
273
218
|
_embedding_generator = EmbeddingGenerator()
|
|
274
219
|
return _embedding_generator
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def reset_embedding_generator() -> None:
|
|
223
|
+
"""Reset the global embedding generator (for testing)."""
|
|
224
|
+
global _embedding_generator
|
|
225
|
+
_embedding_generator = None
|