Skip to content

Commit d988286

Browse files
authored
Merge pull request AmadeusITGroup#69 from MarouaneBenabdelkader/feat/bedrock-titan-embedding
feat: add bedrock-titan-embedding skill for AWS Bedrock Titan V2
2 parents fe504e4 + 0a300f8 commit d988286

6 files changed

Lines changed: 127 additions & 0 deletions

File tree

docs/readme/indexer-skills.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,22 @@ Generates embeddings from text using `llama_index` library.
265265
type: embedding
266266
name: llama-fastembed
267267
```
268+
269+
### AWS Bedrock Titan
270+
Generates embeddings using AWS Bedrock's Titan Embed Text v2 model. AWS credentials are resolved from the standard boto3 credential chain (env vars `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_SESSION_TOKEN`, `AWS_PROFILE`, IAM role, `~/.aws/credentials`, etc.) — do not put them in the YAML.
271+
272+
```yaml
273+
- skill: &BedrockTitanEmbedding
274+
type: embedding
275+
name: bedrock-titan-embedding
276+
params:
277+
region: us-east-1 # Optional: falls back to AWS_REGION / default profile region
278+
model_id: amazon.titan-embed-text-v2:0 # Optional (default)
279+
dimensions: 1024 # Optional: 256 | 512 | 1024 (default 1024)
280+
normalize: true # Optional (default true)
281+
max_retries: 3 # Optional (default 3)
282+
retry_backoff: 2 # Optional seconds, linear per attempt (default 2)
283+
```
268284
</details>
269285

270286

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ dependencies = [
3737
"unstructured>=0.14.8",
3838
"faiss-cpu>=1.11.0",
3939
"langchain_community>=0.3.18",
40+
"boto3>=1.34.0",
4041
]
4142

4243
[project.scripts]

src/docs2vecs/subcommands/indexer/config/config_schema.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,28 @@ definitions:
237237
type: string
238238
deployment_name:
239239
type: string
240+
# BedrockTitanEmbeddingSkill params
241+
region:
242+
type: string
243+
required: False
244+
model_id:
245+
type: string
246+
required: False
247+
dimensions:
248+
type: integer
249+
required: False
250+
allowed: [256, 512, 1024]
251+
normalize:
252+
type: boolean
253+
required: False
254+
max_retries:
255+
type: integer
256+
required: False
257+
min: 1
258+
retry_backoff:
259+
type: integer
260+
required: False
261+
min: 1
240262

241263
skillset:
242264
type: list

src/docs2vecs/subcommands/indexer/skills/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .teams_qna_loader_skill import TeamsQnALoaderSkill
1717
from .confluence_faq_splitter_skill import ConfluenceFAQSplitter
1818
from .json_writer_skill import JSONWriterSkill
19+
from .bedrock_titan_embedding_skill import BedrockTitanEmbeddingSkill
1920

2021

2122
__all__ = [
@@ -37,4 +38,5 @@
3738
"TeamsQnALoaderSkill",
3839
"ConfluenceFAQSplitter",
3940
"JSONWriterSkill",
41+
"BedrockTitanEmbeddingSkill",
4042
]
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import json
2+
import time
3+
from typing import List, Optional
4+
5+
import boto3
6+
7+
from docs2vecs.subcommands.indexer.config.config import Config
8+
from docs2vecs.subcommands.indexer.document.document import Document
9+
from docs2vecs.subcommands.indexer.skills.skill import IndexerSkill
10+
11+
12+
class BedrockTitanEmbeddingSkill(IndexerSkill):
13+
DEFAULT_MODEL_ID = "amazon.titan-embed-text-v2:0"
14+
DEFAULT_DIMENSIONS = 1024
15+
DEFAULT_MAX_RETRIES = 3
16+
DEFAULT_RETRY_BACKOFF = 2
17+
18+
def __init__(self, config: dict, global_config: Config):
19+
super().__init__(config, global_config)
20+
self._model_id = self._config.get("model_id", self.DEFAULT_MODEL_ID)
21+
self._dimensions = self._config.get("dimensions", self.DEFAULT_DIMENSIONS)
22+
self._normalize = self._config.get("normalize", True)
23+
self._max_retries = self._config.get("max_retries", self.DEFAULT_MAX_RETRIES)
24+
self._retry_backoff = self._config.get("retry_backoff", self.DEFAULT_RETRY_BACKOFF)
25+
self._client = boto3.client(
26+
"bedrock-runtime",
27+
region_name=self._config.get("region"),
28+
)
29+
30+
def _embed_text(self, content: str, chunk_id=None):
31+
self.logger.debug(
32+
f"Requesting Bedrock embedding for chunk_id={chunk_id}, content_length={len(content)}"
33+
)
34+
body = json.dumps(
35+
{
36+
"inputText": content,
37+
"dimensions": self._dimensions,
38+
"normalize": self._normalize,
39+
}
40+
)
41+
for attempt in range(self._max_retries):
42+
try:
43+
resp = self._client.invoke_model(
44+
modelId=self._model_id,
45+
body=body,
46+
contentType="application/json",
47+
accept="application/json",
48+
)
49+
embedding = json.loads(resp["body"].read())["embedding"]
50+
self.logger.debug(
51+
f"Successfully received embedding for chunk_id={chunk_id}, embedding_dim={len(embedding) if embedding else 0}"
52+
)
53+
return embedding
54+
except Exception as exc:
55+
if attempt == self._max_retries - 1:
56+
raise
57+
wait = self._retry_backoff * (attempt + 1)
58+
self.logger.warning(
59+
f"Bedrock call failed (attempt {attempt + 1}/{self._max_retries}): {exc} - retrying in {wait}s"
60+
)
61+
time.sleep(wait)
62+
63+
def run(self, input: Optional[List[Document]] = None) -> Optional[List[Document]]:
64+
self.logger.info(
65+
f"Running Bedrock Titan Embedding Skill with model_id: {self._model_id}..."
66+
)
67+
68+
docs_count = len(input)
69+
chunks_count = sum(len(doc.chunks) for doc in input)
70+
71+
self.logger.info(
72+
f"Processing a total of documents: {docs_count}. Total number of chunks: {chunks_count}"
73+
)
74+
75+
for doc in input:
76+
self.logger.debug(f"Processing document: {doc.filename}")
77+
for chunk in doc.chunks:
78+
self.logger.debug(f"Creating embedding for chunk: {chunk.chunk_id}")
79+
chunk.embedding = [] if not chunk.content else self._embed_text(
80+
chunk.content, chunk_id=chunk.chunk_id
81+
)
82+
83+
return input

src/docs2vecs/subcommands/indexer/skills/factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from docs2vecs.subcommands.indexer.skills.confluence_faq_splitter_skill import ConfluenceFAQSplitter
2020
from docs2vecs.subcommands.indexer.skills.teams_qna_loader_skill import TeamsQnALoaderSkill
2121
from docs2vecs.subcommands.indexer.skills.json_writer_skill import JSONWriterSkill
22+
from docs2vecs.subcommands.indexer.skills.bedrock_titan_embedding_skill import BedrockTitanEmbeddingSkill
2223

2324

2425
class SkillType(StrEnum):
@@ -60,6 +61,7 @@ class AvailableSkillName(StrEnum):
6061
# embeddings
6162
AZ_ADA002_EMBEDDING = "azure-ada002-embedding"
6263
LLAMA_FASTEMBED = "llama-fastembed"
64+
BEDROCK_TITAN_EMBEDDING = "bedrock-titan-embedding"
6365

6466
# web loaders
6567
JIRA_LOADER = "jira-loader"
@@ -81,6 +83,7 @@ class AvailableSkillName(StrEnum):
8183
SkillType.EMBEDDING: {
8284
AvailableSkillName.AZ_ADA002_EMBEDDING: AzureAda002EmbeddingSkill,
8385
AvailableSkillName.LLAMA_FASTEMBED: LlamaFastembedEmbeddingSkill,
86+
AvailableSkillName.BEDROCK_TITAN_EMBEDDING: BedrockTitanEmbeddingSkill,
8487
},
8588
SkillType.VECTOR_STORE: {
8689
AvailableSkillName.AZ_AISearch: AzureVectorStoreSkill,

0 commit comments

Comments
 (0)