Skip to content

Remove LlamaIndex as mandatory runtime dependency#322

Draft
mykola-pereyma wants to merge 8 commits into
awslabs:mainfrom
mykola-pereyma:refactor-llama-index-impl
Draft

Remove LlamaIndex as mandatory runtime dependency#322
mykola-pereyma wants to merge 8 commits into
awslabs:mainfrom
mykola-pereyma:refactor-llama-index-impl

Conversation

@mykola-pereyma

Copy link
Copy Markdown
Collaborator

Remove LlamaIndex as Mandatory Runtime Dependency

Motivation

LlamaIndex as a mandatory dependency creates two blocking issues for production deployments:

  1. aiobotocore pin conflict (Dependency conflict: aiobotocore pins botocore to narrow ranges incompatible with bedrock-agentcore #299): LlamaIndex transitive dependency tree pins aiobotocore to a version range incompatible with bedrock-agentcore, making it impossible to use graphrag-toolkit in Bedrock Agent environments.

  2. Deployment footprint (Retrieval requires very large dependencies #91): llama-index-core pulls ~700MB of transitive dependencies, pushing deployments beyond Lambda 250MB limit and adding cold-start latency to EKS containers.

Approach

Introduce a lean core/ module providing the exact interfaces the toolkit needs, implemented directly against boto3:

  • Custom types (Pydantic BaseModel): Node, Document, NodeRef, QueryBundle, NodeWithScore, Response
  • Custom providers (boto3 converse/invoke_model): BedrockLLMProvider, BedrockEmbeddingProvider
  • Custom splitters (tiktoken cl100k_base): SentenceSplitter, TokenTextSplitter
  • ABCs: LLMProvider, EmbeddingProvider, Retriever, Extractor, Transform, PostProcessor, QueryEngine

LlamaIndex becomes optional via extras ([readers], [chunking], [opensearch]).

Key Design Decisions

  1. Anti-Corruption Layer at IdRewriter boundary — single point normalizes LlamaIndex nodes to our types
  2. Behavioral parity — custom SentenceSplitter produces chunk-for-chunk identical output (verified by comparison tests), preserving existing knowledge graph compatibility
  3. Pydantic BaseModel — validation, serialization, backward-compat via model_validator
  4. Optional extras — zero breaking changes for existing users

Challenges Addressed

  • Enum key compatibility (LlamaIndex IntEnum vs string keys)
  • Batch inference compatibility (.model property, snake_case kwargs, anthropic message format conversion)
  • Jupyter/async safety (run_async() with ThreadPoolExecutor fallback)
  • ProcessPoolExecutor pickling (__getstate__/__setstate__ for boto3 clients)
  • BatchExtractorBase kwargs propagation (entity_classification_provider, topic_provider)

Test Results

Suite Result
Unit tests (no llama-index) 1733 pass, 27 skip
Unit tests (with llama-index) 1775 pass
Integration: lexical.short 20 pass, 1 skip (GPU)
Integration: lexical.long (batch inference) 4 pass
Integration: lexical.versioning 5 pass
Total integration 29 pass, 1 skip, 0 fail
SentenceSplitter parity Chunk-for-chunk identical to LlamaIndex

Integration tests are UNMODIFIED from main branch — proving full behavioral compatibility across all pipeline phases including batch inference and versioning.

Deployment Footprint

Configuration Size Lambda?
Before (with llama-index-core) ~290 MB Exceeds 250MB
After (without LlamaIndex) ~130-145 MB 100+ MB headroom

For agentic deployments: eliminates aiobotocore conflict, ~150MB smaller, ~50 fewer transitive packages, faster cold start.

Not Covered (Future Work)

  1. Native file readers (PDF/DOCX) — still requires [readers] extra
  2. Native OpenSearch client — still uses llama_index OpensearchVectorClient
  3. Provider diversity — only Bedrock ships; others need ABC implementation
  4. Observability — no internal logging in providers yet
  5. ACL cleanup — removable once [chunking] extra is deprecated

Commits

# Description Files Lines
1 Core module (types, providers, ABCs) 31 +1849
2 Migrate storage and retrieval layers 75 +339/-275
3 Migrate indexing subsystem 72 +563/-391
4 Migrate config, utils, and tests 64 +613/-489
5 Pydantic BaseModel + pyproject.toml 3 +199/-24
6 Custom SentenceSplitter + async utils 2 +272
7 Review fixes (batch compat, retries, message converters) 35 +784/-928

Resolves #299, #91

Introduce graphrag_toolkit.core as the foundation layer replacing
LlamaIndex internal types:

- Node, Document, NodeRef, NodeWithScore, QueryBundle (Pydantic models)
- LLMProvider ABC + BedrockLLMProvider (boto3 converse API)
- EmbeddingProvider ABC + BedrockEmbeddingProvider (boto3 invoke_model)
- PromptTemplate, ChatPromptTemplate
- Retriever, Extractor, Transform, PostProcessor, QueryEngine ABCs
- Pipeline runner, CallbackRegistry
- Compatibility aliases (TextNode=Node, BaseNode=Node)

All new code with comprehensive unit tests.

Part of: awslabs#299, awslabs#91
Replace LlamaIndex imports in storage/ and retrieval/ subsystems:
- VectorStoreQuery, MetadataFilters → core.vector_store_types
- TextNode, NodeWithScore, QueryBundle → core.types
- Retriever, PostProcessor → core ABCs
- QueryEngine → core.query_engine

Mechanical import changes — no behavioral modification.

Part of: awslabs#299, awslabs#91
Replace LlamaIndex imports in indexing/extract, indexing/build,
and indexing/load:
- TextNode, Document → core.types
- Extractor, Transform → core ABCs
- IngestionPipeline → core.pipeline
- PromptTemplate → core.prompt
- SentenceSplitter → core.text_splitter (custom implementation)

Adds Anti-Corruption Layer (indexing/compat/llama_index_adapter.py)
to normalize LlamaIndex nodes at the IdRewriter boundary.

Part of: awslabs#299, awslabs#91
Replace LlamaIndex imports in top-level files and test suite:
- config.py: LLMType/EmbeddingType use core providers
- lexical_graph_index.py: custom SentenceSplitter as default chunker
- lexical_graph_query_engine.py: core Response/QueryBundle types
- LLMCache: wraps BedrockLLMProvider
- Test fixtures: use core.types instead of llama_index.core.schema
- requirements.txt: add tiktoken, remove LlamaIndex from mandatory deps

Part of: awslabs#299, awslabs#91
…ct.toml

- Node, NodeRef, Document, NodeWithScore, QueryBundle → Pydantic BaseModel
- model_validator for backward-compat node_id→id_ mapping
- NodeWithScore.score is Optional[float]
- pyproject.toml: LlamaIndex moved to optional extras
  ([readers], [chunking], [opensearch], [llms])

Part of: awslabs#299, awslabs#91
- SentenceSplitter: token-based chunking using tiktoken cl100k_base,
  produces chunk-for-chunk identical output to LlamaIndex (verified)
- TokenTextSplitter: fixed token-count chunks with overlap
- run_async(): safely handles Jupyter/existing event loops via
  ThreadPoolExecutor fallback

These eliminate llama-index-core as a runtime dependency for the
default chunking and async paths.

Part of: awslabs#299, awslabs#91
Address critical review findings:
- BedrockLLMProvider: add .model property, _get_all_kwargs() returns
  snake_case keys with defaults (max_tokens=4096, temperature=0.0)
- BedrockEmbeddingProvider: add retry config (adaptive, 10 retries, 60s)
- CallbackRegistry: list snapshot in emit() for thread safety
- Extractor: use run_async() instead of bare asyncio.run()

Part of: awslabs#299, awslabs#91
The node_strategy can generate property dicts containing 'id' as a key,
which overwrites the actual node ID when spread into parameters via
**node['properties']. Filter out reserved keys ('id', 'node_id', 'label')
from the property key strategy to prevent false-negative test failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dependency conflict: aiobotocore pins botocore to narrow ranges incompatible with bedrock-agentcore

1 participant