This commit is contained in:
Aidan Petti 2025-12-13 23:20:20 +00:00 committed by GitHub
commit e16fe624d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1227 additions and 64 deletions

View file

@ -29,6 +29,38 @@ from graphiti_core.driver.driver import GraphDriver, GraphDriverSession, GraphPr
logger = logging.getLogger(__name__)
DEFAULT_SIZE = 10
class GraphitiNeptuneGraph(NeptuneGraph):
"""
Custom NeptuneGraph subclass that uses pre-defined Graphiti schema
instead of calling Neptune's expensive statistics API.
"""
# Define Graphiti schema to avoid expensive statistics API calls
GRAPHITI_SCHEMA = """
Node labels: Episodic, Entity, Community
Relationship types: MENTIONS, RELATES_TO, HAS_MEMBER
Node properties:
Episodic {uuid: string, name: string, group_id: string, source: string, source_description: string, content: string, valid_at: datetime, created_at: datetime, entity_edges: list}
Entity {uuid: string, name: string, group_id: string, summary: string, created_at: datetime, name_embedding: string, labels: list}
Community {uuid: string, name: string, group_id: string, summary: string, created_at: datetime, name_embedding: string}
Relationship properties:
MENTIONS {created_at: datetime}
RELATES_TO {uuid: string, name: string, group_id: string, fact: string, fact_embedding: string, episodes: list, created_at: datetime, expired_at: datetime, valid_at: datetime, invalid_at: datetime}
HAS_MEMBER {uuid: string, created_at: datetime}
"""
def _refresh_schema(self) -> None:
"""
Override to use pre-defined schema instead of calling statistics API.
This avoids the expensive Neptune statistics API call that requires
statistics to be enabled on the Neptune instance.
"""
self.schema = self.GRAPHITI_SCHEMA
logger.debug('Using pre-defined Graphiti schema, skipping Neptune statistics API')
aoss_indices = [
{
'index_name': 'node_name_and_summary',
@ -109,7 +141,7 @@ aoss_indices = [
class NeptuneDriver(GraphDriver):
provider: GraphProvider = GraphProvider.NEPTUNE
def __init__(self, host: str, aoss_host: str, port: int = 8182, aoss_port: int = 443):
def __init__(self, host: str, aoss_host: str, port: int = 8182, aoss_port: int = 443, database: str = 'default'):
"""This initializes a NeptuneDriver for use with Neptune as a backend
Args:
@ -117,15 +149,20 @@ class NeptuneDriver(GraphDriver):
aoss_host (str): The OpenSearch host value
port (int, optional): The Neptune Database port, ignored for Neptune Analytics. Defaults to 8182.
aoss_port (int, optional): The OpenSearch port. Defaults to 443.
database (str, optional): The database name (for compatibility with base class). Defaults to 'default'.
"""
if not host:
raise ValueError('You must provide an endpoint to create a NeptuneDriver')
# Set the database attribute required by the base GraphDriver class
self._database = database
if host.startswith('neptune-db://'):
# This is a Neptune Database Cluster
endpoint = host.replace('neptune-db://', '')
self.client = NeptuneGraph(endpoint, port)
logger.debug('Creating Neptune Database session for %s', host)
# Use custom GraphitiNeptuneGraph to avoid expensive statistics API calls
self.client = GraphitiNeptuneGraph(endpoint, port)
logger.debug('Creating Neptune Database session for %s with pre-defined schema', host)
elif host.startswith('neptune-graph://'):
# This is a Neptune Analytics Graph
graphId = host.replace('neptune-graph://', '')
@ -139,9 +176,12 @@ class NeptuneDriver(GraphDriver):
if not aoss_host:
raise ValueError('You must provide an AOSS endpoint to create an OpenSearch driver.')
# Strip protocol prefix from aoss_host if present (OpenSearch expects just the hostname)
aoss_hostname = aoss_host.replace('https://', '').replace('http://', '')
session = boto3.Session()
self.aoss_client = OpenSearch(
hosts=[{'host': aoss_host, 'port': aoss_port}],
hosts=[{'host': aoss_hostname, 'port': aoss_port}],
http_auth=Urllib3AWSV4SignerAuth(
session.get_credentials(), session.region_name, 'aoss'
),
@ -160,30 +200,22 @@ class NeptuneDriver(GraphDriver):
else:
for k, v in params.items():
if isinstance(v, datetime.datetime):
# Convert datetime to ISO format string
params[k] = v.isoformat()
elif isinstance(v, list):
# Handle lists that might contain datetime objects
# Check if list contains actual datetime objects (not just strings with 'T')
has_datetime = any(isinstance(item, datetime.datetime) for item in v)
if has_datetime:
# Convert datetime objects to ISO strings
for i, item in enumerate(v):
if isinstance(item, datetime.datetime):
v[i] = item.isoformat()
# Handle nested dictionaries
for i, item in enumerate(v):
if isinstance(item, datetime.datetime):
v[i] = item.isoformat()
query = str(query).replace(f'${k}', f'datetime(${k})')
if isinstance(item, dict):
query = self._sanitize_parameters(query, v[i])
# If the list contains datetime objects, we need to wrap each element with datetime()
if any(isinstance(item, str) and 'T' in item for item in v):
# Create a new list expression with datetime() wrapped around each element
datetime_list = (
'['
+ ', '.join(
f'datetime("{item}")'
if isinstance(item, str) and 'T' in item
else repr(item)
for item in v
)
+ ']'
)
query = str(query).replace(f'${k}', datetime_list)
elif isinstance(v, dict):
query = self._sanitize_parameters(query, v)
return query
@ -192,6 +224,13 @@ class NeptuneDriver(GraphDriver):
self, cypher_query_, **kwargs: Any
) -> tuple[dict[str, Any], None, None]:
params = dict(kwargs)
# Flatten nested 'params' dict if present (for compatibility with Neo4j driver interface)
if 'params' in params and isinstance(params['params'], dict):
nested_params = params.pop('params')
# Merge nested params into the top level, nested params take precedence
params = {**params, **nested_params}
if isinstance(cypher_query_, list):
for q in cypher_query_:
result, _, _ = self._run_query(q[0], q[1])

View file

@ -122,7 +122,7 @@ EPISODIC_NODE_RETURN_NEPTUNE = """
e.group_id AS group_id,
e.source_description AS source_description,
e.source AS source,
split(e.entity_edges, ",") AS entity_edges
split(e.entity_edges, "|") AS entity_edges
"""

View file

@ -595,7 +595,7 @@ class CommunityNode(Node):
async def save(self, driver: GraphDriver):
if driver.provider == GraphProvider.NEPTUNE:
await driver.save_to_aoss( # pyright: ignore reportAttributeAccessIssue
'communities',
'community_name',
[{'name': self.name, 'uuid': self.uuid, 'group_id': self.group_id}],
)
result = await driver.execute_query(

View file

@ -87,6 +87,11 @@ def node_search_filter_query_constructor(
if provider == GraphProvider.KUZU:
node_label_filter = 'list_has_all(n.labels, $labels)'
filter_params['labels'] = filters.node_labels
elif provider == GraphProvider.NEPTUNE:
# Neptune doesn't support pipe operator in WHERE clause
# Use OR with separate label checks instead
label_conditions = [f'n:{label}' for label in filters.node_labels]
node_label_filter = '(' + ' OR '.join(label_conditions) + ')'
else:
node_labels = '|'.join(filters.node_labels)
node_label_filter = 'n:' + node_labels
@ -130,6 +135,15 @@ def edge_search_filter_query_constructor(
'list_has_all(n.labels, $labels) AND list_has_all(m.labels, $labels)'
)
filter_params['labels'] = filters.node_labels
elif provider == GraphProvider.NEPTUNE:
# Neptune doesn't support pipe operator in WHERE clause
# Use OR with separate label checks instead
n_label_conditions = [f'n:{label}' for label in filters.node_labels]
m_label_conditions = [f'm:{label}' for label in filters.node_labels]
node_label_filter = (
'(' + ' OR '.join(n_label_conditions) + ') AND ('
+ ' OR '.join(m_label_conditions) + ')'
)
else:
node_labels = '|'.join(filters.node_labels)
node_label_filter = 'n:' + node_labels + ' AND m:' + node_labels

View file

@ -1075,7 +1075,7 @@ async def community_similarity_search(
comm.name AS name,
comm.created_at AS created_at,
comm.summary AS summary,
comm.name_embedding AS name_embedding
[x IN split(comm.name_embedding, ",") | toFloat(x)] AS name_embedding
ORDER BY i.score DESC
LIMIT $limit
"""
@ -1887,7 +1887,7 @@ async def get_embeddings_for_nodes(
WHERE n.uuid IN $node_uuids
RETURN DISTINCT
n.uuid AS uuid,
split(n.name_embedding, ",") AS name_embedding
[x IN split(n.name_embedding, ",") | toFloat(x)] AS name_embedding
"""
else:
query = """
@ -1922,7 +1922,7 @@ async def get_embeddings_for_communities(
WHERE c.uuid IN $community_uuids
RETURN DISTINCT
c.uuid AS uuid,
split(c.name_embedding, ",") AS name_embedding
[x IN split(c.name_embedding, ",") | toFloat(x)] AS name_embedding
"""
else:
query = """
@ -1959,7 +1959,7 @@ async def get_embeddings_for_edges(
WHERE e.uuid IN $edge_uuids
RETURN DISTINCT
e.uuid AS uuid,
split(e.fact_embedding, ",") AS fact_embedding
[x IN split(e.fact_embedding, ",") | toFloat(x)] AS fact_embedding
"""
else:
match_query = """

View file

@ -0,0 +1,131 @@
# Graphiti MCP Server Configuration for AWS Neptune
# This configuration is optimized for AWS Neptune Database or Analytics
server:
transport: "http" # HTTP transport (SSE is deprecated)
host: "0.0.0.0"
port: 8000
llm:
provider: "openai" # Options: openai, azure_openai, anthropic, gemini, groq
model: "gpt-5-mini"
max_tokens: 4096
providers:
openai:
api_key: ${OPENAI_API_KEY}
api_url: ${OPENAI_API_URL:https://api.openai.com/v1}
organization_id: ${OPENAI_ORGANIZATION_ID:}
azure_openai:
api_key: ${AZURE_OPENAI_API_KEY}
api_url: ${AZURE_OPENAI_ENDPOINT}
api_version: ${AZURE_OPENAI_API_VERSION:2024-10-21}
deployment_name: ${AZURE_OPENAI_DEPLOYMENT}
use_azure_ad: ${USE_AZURE_AD:false}
anthropic:
api_key: ${ANTHROPIC_API_KEY}
api_url: ${ANTHROPIC_API_URL:https://api.anthropic.com}
max_retries: 3
gemini:
api_key: ${GOOGLE_API_KEY}
project_id: ${GOOGLE_PROJECT_ID:}
location: ${GOOGLE_LOCATION:us-central1}
groq:
api_key: ${GROQ_API_KEY}
api_url: ${GROQ_API_URL:https://api.groq.com/openai/v1}
embedder:
provider: "openai" # Options: openai, azure_openai, gemini, voyage
model: "text-embedding-3-small"
dimensions: 1536
providers:
openai:
api_key: ${OPENAI_API_KEY}
api_url: ${OPENAI_API_URL:https://api.openai.com/v1}
organization_id: ${OPENAI_ORGANIZATION_ID:}
azure_openai:
api_key: ${AZURE_OPENAI_API_KEY}
api_url: ${AZURE_OPENAI_EMBEDDINGS_ENDPOINT}
api_version: ${AZURE_OPENAI_API_VERSION:2024-10-21}
deployment_name: ${AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT}
use_azure_ad: ${USE_AZURE_AD:false}
gemini:
api_key: ${GOOGLE_API_KEY}
project_id: ${GOOGLE_PROJECT_ID:}
location: ${GOOGLE_LOCATION:us-central1}
voyage:
api_key: ${VOYAGE_API_KEY}
api_url: ${VOYAGE_API_URL:https://api.voyageai.com/v1}
model: "voyage-3"
database:
provider: "neptune" # Using AWS Neptune for this configuration
providers:
neptune:
# Neptune Graph Endpoint
# For Neptune Database: neptune-db://your-cluster.region.neptune.amazonaws.com
# For Neptune Analytics: neptune-graph://g-your-graph-id
host: ${NEPTUNE_HOST:neptune-db://localhost}
# Amazon OpenSearch Serverless (AOSS) Endpoint for full-text search
# Required for hybrid search capabilities
# Example: your-collection.us-east-1.aoss.amazonaws.com
aoss_host: ${AOSS_HOST}
# Neptune connection port (default: 8182 for Neptune Database)
port: ${NEPTUNE_PORT:8182}
# AOSS connection port (default: 443 for HTTPS)
aoss_port: ${AOSS_PORT:443}
# AWS Region (e.g., us-east-1, us-west-2)
# If not specified, will be detected from AWS CLI or instance metadata
region: ${AWS_REGION:}
# AWS Credentials:
# Neptune uses the standard AWS credential chain:
# 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)
# 2. AWS credentials file (~/.aws/credentials)
# 3. AWS CLI configuration (aws configure)
# 4. IAM role (when running on EC2, ECS, Lambda, etc.)
#
# Configure credentials using one of these methods:
# - Run: aws configure
# - Set environment variables:
# export AWS_ACCESS_KEY_ID="your-access-key"
# export AWS_SECRET_ACCESS_KEY="your-secret-key"
# export AWS_SESSION_TOKEN="your-session-token" # Optional for temporary credentials
# - Use IAM role when running on AWS infrastructure
graphiti:
group_id: ${GRAPHITI_GROUP_ID:main}
episode_id_prefix: ${EPISODE_ID_PREFIX:}
user_id: ${USER_ID:mcp_user}
entity_types:
- name: "Preference"
description: "User preferences, choices, opinions, or selections (PRIORITIZE over most other types except User/Assistant)"
- name: "Requirement"
description: "Specific needs, features, or functionality that must be fulfilled"
- name: "Procedure"
description: "Standard operating procedures and sequential instructions"
- name: "Location"
description: "Physical or virtual places where activities occur"
- name: "Event"
description: "Time-bound activities, occurrences, or experiences"
- name: "Organization"
description: "Companies, institutions, groups, or formal entities"
- name: "Document"
description: "Information content in various forms (books, articles, reports, etc.)"
- name: "Topic"
description: "Subject of conversation, interest, or knowledge domain (use as last resort)"
- name: "Object"
description: "Physical items, tools, devices, or possessions (use as last resort)"

View file

@ -71,7 +71,7 @@ embedder:
model: "voyage-3"
database:
provider: "falkordb" # Default: falkordb. Options: neo4j, falkordb
provider: "falkordb" # Default: falkordb. Options: neo4j, falkordb, neptune
providers:
falkordb:
@ -86,6 +86,13 @@ database:
database: ${NEO4J_DATABASE:neo4j}
use_parallel_runtime: ${USE_PARALLEL_RUNTIME:false}
neptune:
host: ${NEPTUNE_HOST:neptune-db://localhost}
aoss_host: ${AOSS_HOST}
port: ${NEPTUNE_PORT:8182}
aoss_port: ${AOSS_PORT:443}
region: ${AWS_REGION:}
graphiti:
group_id: ${GRAPHITI_GROUP_ID:main}
episode_id_prefix: ${EPISODE_ID_PREFIX:}

View file

@ -33,16 +33,17 @@ ENV UV_COMPILE_BYTECODE=1 \
WORKDIR /app/mcp
# Accept graphiti-core version as build argument
ARG GRAPHITI_CORE_VERSION=0.23.1
ARG GRAPHITI_CORE_VERSION=0.24.2
# Copy project files for dependency installation
COPY pyproject.toml uv.lock ./
# Remove the local path override for graphiti-core in Docker builds
# Install with falkordb and neptune extras for maximum flexibility
# and regenerate lock file to match the PyPI version
RUN sed -i '/\[tool\.uv\.sources\]/,/graphiti-core/d' pyproject.toml && \
if [ -n "${GRAPHITI_CORE_VERSION}" ]; then \
sed -i "s/graphiti-core\[falkordb\]>=[0-9]\+\.[0-9]\+\.[0-9]\+$/graphiti-core[falkordb]==${GRAPHITI_CORE_VERSION}/" pyproject.toml; \
sed -i "s/graphiti-core\[falkordb\]>=[0-9]\+\.[0-9]\+\.[0-9]\+$/graphiti-core[falkordb,neptune]==${GRAPHITI_CORE_VERSION}/" pyproject.toml; \
fi && \
echo "Regenerating lock file for PyPI graphiti-core..." && \
rm -f uv.lock && \
@ -112,7 +113,7 @@ EOF
RUN chmod +x /start-services.sh
# Add Docker labels with version information
ARG MCP_SERVER_VERSION=1.0.1
ARG MCP_SERVER_VERSION=1.0.2
ARG BUILD_DATE
ARG VCS_REF
LABEL org.opencontainers.image.title="FalkorDB + Graphiti MCP Server" \

View file

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
# Standalone Graphiti MCP Server Image
# This image runs only the MCP server and connects to an external database (Neo4j or FalkorDB)
# This image runs only the MCP server and connects to an external database (Neo4j, FalkorDB or Neptune)
FROM python:3.11-slim-bookworm
@ -28,16 +28,16 @@ ENV UV_COMPILE_BYTECODE=1 \
WORKDIR /app/mcp
# Accept graphiti-core version as build argument
ARG GRAPHITI_CORE_VERSION=0.23.1
ARG GRAPHITI_CORE_VERSION=0.24.2
# Copy project files for dependency installation
COPY pyproject.toml uv.lock ./
# Remove the local path override for graphiti-core in Docker builds
# Install with BOTH neo4j and falkordb extras for maximum flexibility
# Install with neo4j, falkordb, and neptune extras for maximum flexibility
# and regenerate lock file to match the PyPI version
RUN sed -i '/\[tool\.uv\.sources\]/,/graphiti-core/d' pyproject.toml && \
sed -i "s/graphiti-core\[falkordb\]>=[0-9]\+\.[0-9]\+\.[0-9]\+$/graphiti-core[neo4j,falkordb]==${GRAPHITI_CORE_VERSION}/" pyproject.toml && \
sed -i "s/graphiti-core\[falkordb\]>=[0-9]\+\.[0-9]\+\.[0-9]\+$/graphiti-core[neo4j,falkordb,neptune]==${GRAPHITI_CORE_VERSION}/" pyproject.toml && \
echo "Regenerating lock file for PyPI graphiti-core..." && \
rm -f uv.lock && \
uv lock
@ -58,7 +58,7 @@ COPY config/ ./config/
RUN mkdir -p /var/log/graphiti
# Add Docker labels with version information
ARG MCP_SERVER_VERSION=1.0.1
ARG MCP_SERVER_VERSION=1.0.2
ARG BUILD_DATE
ARG VCS_REF
LABEL org.opencontainers.image.title="Graphiti MCP Server (Standalone)" \

View file

@ -1,13 +1,13 @@
[project]
name = "mcp-server"
version = "1.0.1"
version = "1.0.2"
description = "Graphiti MCP Server"
readme = "README.md"
requires-python = ">=3.10,<4"
dependencies = [
"mcp>=1.9.4",
"openai>=1.91.0",
"graphiti-core[falkordb]>=0.23.1",
"graphiti-core[falkordb,neptune]>=0.24.2",
"pydantic-settings>=2.0.0",
"pyyaml>=6.0",
"typing-extensions>=4.0.0",

View file

@ -191,11 +191,62 @@ class FalkorDBProviderConfig(BaseModel):
database: str = 'default_db'
class NeptuneProviderConfig(BaseModel):
"""Neptune provider configuration."""
host: str = 'neptune-db://localhost'
aoss_host: str | None = None
port: int = Field(default=8182, ge=1, le=65535)
aoss_port: int = Field(default=443, ge=1, le=65535)
region: str | None = None
def model_post_init(self, __context) -> None:
"""Validate and normalize Neptune-specific requirements."""
# Auto-detect and add protocol if missing
if not self.host.startswith(
('neptune-db://', 'neptune-graph://', 'bolt://', 'http://', 'https://')
):
# Check if it looks like a Neptune Analytics graph ID (starts with 'g-')
if self.host.startswith('g-'):
self.host = f'neptune-graph://{self.host}'
# Check if it contains 'neptune.amazonaws.com' (Neptune Database cluster)
elif 'neptune.amazonaws.com' in self.host:
self.host = f'neptune-db://{self.host}'
# Otherwise default to Neptune Database protocol
else:
self.host = f'neptune-db://{self.host}'
# Validate protocol is correct
if not self.host.startswith(('neptune-db://', 'neptune-graph://')):
raise ValueError(
'Neptune host must start with neptune-db:// or neptune-graph://\n'
'Examples:\n'
' - Database: neptune-db://my-cluster.us-east-1.neptune.amazonaws.com\n'
' - Analytics: neptune-graph://g-abc123xyz'
)
if not self.aoss_host:
raise ValueError(
'Neptune requires aoss_host for full-text search.\n'
'Set AOSS_HOST environment variable or add to config:\n'
' database:\n'
' providers:\n'
' neptune:\n'
' aoss_host: "your-aoss-endpoint.us-east-1.aoss.amazonaws.com"\n'
'Note: Provide hostname only, without https:// prefix'
)
# Strip protocol prefix from aoss_host if present (OpenSearch expects just the hostname)
if self.aoss_host.startswith(('https://', 'http://')):
self.aoss_host = self.aoss_host.replace('https://', '').replace('http://', '')
class DatabaseProvidersConfig(BaseModel):
"""Database providers configuration."""
neo4j: Neo4jProviderConfig | None = None
falkordb: FalkorDBProviderConfig | None = None
neptune: NeptuneProviderConfig | None = None
class DatabaseConfig(BaseModel):

View file

@ -228,6 +228,23 @@ class GraphitiService:
embedder=embedder_client,
max_coroutines=self.semaphore_limit,
)
elif self.config.database.provider.lower() == 'neptune':
# For Neptune, create a NeptuneDriver instance directly
from graphiti_core.driver.neptune_driver import NeptuneDriver
neptune_driver = NeptuneDriver(
host=db_config['host'],
aoss_host=db_config['aoss_host'],
port=db_config['port'],
aoss_port=db_config['aoss_port'],
)
self.client = Graphiti(
graph_driver=neptune_driver,
llm_client=llm_client,
embedder=embedder_client,
max_coroutines=self.semaphore_limit,
)
else:
# For Neo4j (default), use the original approach
self.client = Graphiti(
@ -266,6 +283,25 @@ class GraphitiService:
f' - Or run Neo4j manually: docker run -p 7474:7474 -p 7687:7687 neo4j:latest\n\n'
f'{"=" * 70}\n'
) from db_error
elif db_provider.lower() == 'neptune':
raise RuntimeError(
f'\n{"=" * 70}\n'
f'Database Connection Error: Neptune is not accessible\n'
f'{"=" * 70}\n\n'
f'Neptune: {db_config.get("host", "unknown")}\n'
f'OpenSearch: {db_config.get("aoss_host", "unknown")}\n\n'
f'Troubleshooting:\n'
f' 1. Verify Neptune endpoint format (neptune-db:// or neptune-graph://)\n'
f' 2. Check AWS credentials: aws sts get-caller-identity\n'
f' 3. Verify security groups allow access\n'
f' 4. Confirm OpenSearch Serverless endpoint is accessible\n'
f' 5. Check IAM permissions (neptune-db:*, aoss:*, neptune-graph:*)\n\n'
f'AWS Setup:\n'
f' - Region: {db_config.get("region", "not set")}\n'
f' - Run: aws neptune describe-db-clusters (for Database)\n'
f' - Run: aws neptune-graph list-graphs (for Analytics)\n\n'
f'{"=" * 70}\n'
) from db_error
else:
raise RuntimeError(
f'\n{"=" * 70}\n'
@ -806,7 +842,7 @@ async def initialize_server() -> ServerConfig:
)
parser.add_argument(
'--database-provider',
choices=['neo4j', 'falkordb'],
choices=['neo4j', 'falkordb', 'neptune'],
help='Database provider to use',
)

View file

@ -16,6 +16,14 @@ try:
except ImportError:
HAS_FALKOR = False
# Try to import NeptuneDriver if available
try:
from graphiti_core.driver.neptune_driver import NeptuneDriver # noqa: F401
HAS_NEPTUNE = True
except ImportError:
HAS_NEPTUNE = False
# Kuzu support removed - FalkorDB is now the default
from graphiti_core.embedder import EmbedderClient, OpenAIEmbedder
from graphiti_core.llm_client import LLMClient, OpenAIClient
@ -433,5 +441,103 @@ class DatabaseDriverFactory:
'database': falkor_config.database,
}
case 'neptune':
if not HAS_NEPTUNE:
raise ValueError(
'Neptune driver not available. Install with:\n'
' pip install graphiti-core[neptune]\n'
'or:\n'
' uv add graphiti-core[neptune]'
)
# Validate AWS credentials early
import boto3
try:
session = boto3.Session()
credentials = session.get_credentials()
if not credentials:
raise ValueError(
'AWS credentials not configured for Neptune.\n'
'Configure using one of:\n'
' 1. AWS CLI: aws configure\n'
' 2. Environment: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY\n'
' 3. IAM role (if running on AWS)\n'
' 4. Credentials file: ~/.aws/credentials'
)
region = session.region_name
if not region:
import logging
logger = logging.getLogger(__name__)
logger.warning('AWS region not detected, using default from config')
except Exception as e:
raise ValueError(f'AWS credential error: {e}') from e
# Load Neptune config and environment variables
import os
# Read environment variables first
env_host = os.environ.get('NEPTUNE_HOST')
env_aoss_host = os.environ.get('AOSS_HOST')
env_port = os.environ.get('NEPTUNE_PORT')
env_aoss_port = os.environ.get('AOSS_PORT')
env_region = os.environ.get('AWS_REGION')
if config.providers.neptune:
neptune_config = config.providers.neptune
# Apply environment overrides
host = env_host or neptune_config.host
aoss_host = env_aoss_host or neptune_config.aoss_host
port = int(env_port) if env_port else neptune_config.port
aoss_port = int(env_aoss_port) if env_aoss_port else neptune_config.aoss_port
region_override = env_region or region or neptune_config.region
else:
# No config provided, use environment variables with defaults
from config.schema import NeptuneProviderConfig
host = env_host or 'neptune-db://localhost'
aoss_host = env_aoss_host
port = int(env_port) if env_port else 8182
aoss_port = int(env_aoss_port) if env_aoss_port else 443
region_override = env_region or region
# Create config with values to trigger validation
neptune_config = NeptuneProviderConfig(
host=host,
aoss_host=aoss_host,
port=port,
aoss_port=aoss_port,
region=region_override,
)
# Use normalized values from config (protocol may have been auto-added)
host = neptune_config.host
aoss_host = neptune_config.aoss_host
port = neptune_config.port
aoss_port = neptune_config.aoss_port
region_override = neptune_config.region
if not aoss_host:
raise ValueError(
'Neptune requires AOSS_HOST for full-text search.\n'
'Set it in config or environment variable.'
)
import logging
logger = logging.getLogger(__name__)
logger.info(f'Creating Neptune driver for {host} with region {region_override}')
return {
'driver': 'neptune',
'host': host,
'aoss_host': aoss_host,
'port': port,
'aoss_port': aoss_port,
'region': region_override,
}
case _:
raise ValueError(f'Unsupported Database provider: {provider}')

View file

@ -6,9 +6,6 @@ import os
import sys
from pathlib import Path
# Add the current directory to the path
sys.path.insert(0, str(Path(__file__).parent.parent / 'src'))
from config.schema import GraphitiConfig
from services.factories import DatabaseDriverFactory, EmbedderFactory, LLMClientFactory

View file

@ -0,0 +1,303 @@
#!/usr/bin/env python3
"""
Tests for Neptune database provider configuration and validation.
These tests validate Neptune-specific configuration requirements including:
- Endpoint format validation (neptune-db:// and neptune-graph://)
- AOSS host requirement validation
- AWS credential validation (mocked)
- Environment variable overrides
- Factory creation with Neptune provider
"""
import os
from pathlib import Path
from unittest.mock import MagicMock, patch
from config.schema import DatabaseProvidersConfig, GraphitiConfig, NeptuneProviderConfig
from services.factories import DatabaseDriverFactory
def test_neptune_provider_config_validation():
"""Test NeptuneProviderConfig validation rules."""
print('\nTesting Neptune provider configuration validation...')
# Test valid Database endpoint
try:
config = NeptuneProviderConfig(
host='neptune-db://my-cluster.us-east-1.neptune.amazonaws.com',
aoss_host='my-aoss.us-east-1.aoss.amazonaws.com',
port=8182,
aoss_port=443,
)
print('✓ Valid Database endpoint accepted')
assert config.host.startswith('neptune-db://')
except Exception as e:
print(f'✗ Failed to accept valid Database endpoint: {e}')
raise
# Test valid Analytics endpoint
try:
config = NeptuneProviderConfig(
host='neptune-graph://g-abc123xyz',
aoss_host='my-aoss.us-east-1.aoss.amazonaws.com',
)
print('✓ Valid Analytics endpoint accepted')
assert config.host.startswith('neptune-graph://')
except Exception as e:
print(f'✗ Failed to accept valid Analytics endpoint: {e}')
raise
# Test invalid endpoint format
try:
config = NeptuneProviderConfig(
host='https://my-neptune.com',
aoss_host='my-aoss.us-east-1.aoss.amazonaws.com',
)
print('✗ Invalid endpoint format should have been rejected')
raise AssertionError('Expected ValueError for invalid endpoint format')
except ValueError as e:
print(f'✓ Invalid endpoint format rejected: {str(e)[:60]}...')
assert 'must start with neptune-db:// or neptune-graph://' in str(e)
# Test missing AOSS host
try:
config = NeptuneProviderConfig(
host='neptune-db://my-cluster.us-east-1.neptune.amazonaws.com',
aoss_host=None,
)
print('✗ Missing AOSS host should have been rejected')
raise AssertionError('Expected ValueError for missing AOSS host')
except ValueError as e:
print(f'✓ Missing AOSS host rejected: {str(e)[:60]}...')
assert 'requires aoss_host' in str(e)
# Test port range validation
try:
config = NeptuneProviderConfig(
host='neptune-db://my-cluster.us-east-1.neptune.amazonaws.com',
aoss_host='my-aoss.us-east-1.aoss.amazonaws.com',
port=70000, # Invalid port
)
print('✗ Invalid port should have been rejected')
raise AssertionError('Expected ValidationError for invalid port')
except Exception as e:
print(f'✓ Invalid port rejected: {str(e)[:60]}...')
print('✓ Neptune provider configuration validation complete')
def test_neptune_environment_overrides():
"""Test Neptune configuration with environment variable overrides."""
print('\nTesting Neptune environment variable overrides...')
# Set Neptune environment variables
test_env = {
'NEPTUNE_HOST': 'neptune-db://test-cluster.us-west-2.neptune.amazonaws.com',
'AOSS_HOST': 'test-aoss.us-west-2.aoss.amazonaws.com',
'NEPTUNE_PORT': '9999',
'AOSS_PORT': '9443',
'AWS_REGION': 'us-west-2',
}
with patch.dict(os.environ, test_env, clear=False):
try:
# Load config with environment overrides
config_path = Path(__file__).parent.parent / 'config' / 'config.yaml'
config = GraphitiConfig(_env_file=None, config_path=str(config_path))
print('✓ Loaded configuration with environment overrides')
# Verify environment variables were applied
if config.database.providers and config.database.providers.neptune:
neptune_config = config.database.providers.neptune
print(f' Neptune host: {neptune_config.host}')
print(f' AOSS host: {neptune_config.aoss_host}')
print(f' Neptune port: {neptune_config.port}')
print(f' AOSS port: {neptune_config.aoss_port}')
print(f' Region: {neptune_config.region}')
# Verify the overrides were applied correctly
assert neptune_config.host == test_env['NEPTUNE_HOST']
assert neptune_config.aoss_host == test_env['AOSS_HOST']
assert neptune_config.port == 9999
assert neptune_config.aoss_port == 9443
assert neptune_config.region == test_env['AWS_REGION']
print('✓ All environment overrides applied correctly')
else:
print('⚠ Neptune provider not configured, skipping validation')
except Exception as e:
print(f'✗ Failed to load configuration with environment overrides: {e}')
raise
print('✓ Neptune environment override tests complete')
def test_neptune_factory_creation_with_mock_credentials():
"""Test Neptune factory creation with mocked AWS credentials."""
print('\nTesting Neptune factory creation with mocked credentials...')
# Mock AWS credentials
mock_credentials = MagicMock()
mock_credentials.access_key = 'mock_access_key'
mock_credentials.secret_key = 'mock_secret_key'
mock_credentials.token = None
mock_session = MagicMock()
mock_session.get_credentials.return_value = mock_credentials
mock_session.region_name = 'us-east-1'
# Create test configuration
test_config = {
'database': {
'provider': 'neptune',
'providers': {
'neptune': {
'host': 'neptune-db://test-cluster.us-east-1.neptune.amazonaws.com',
'aoss_host': 'test-aoss.us-east-1.aoss.amazonaws.com',
'port': 8182,
'aoss_port': 443,
'region': 'us-east-1',
}
},
}
}
with patch('boto3.Session', return_value=mock_session):
try:
# Create database config using factory
config = DatabaseProvidersConfig(**test_config['database']['providers'])
print('✓ Created DatabaseProvidersConfig with Neptune')
# Verify Neptune config was created
assert config.neptune is not None
print('✓ Neptune provider config exists')
# Create driver config from factory
db_config = DatabaseDriverFactory.create_config(test_config)
print('✓ Created driver config from factory')
# Verify config contains expected Neptune parameters
assert db_config['driver'] == 'neptune'
assert db_config['host'] == 'neptune-db://test-cluster.us-east-1.neptune.amazonaws.com'
assert db_config['aoss_host'] == 'test-aoss.us-east-1.aoss.amazonaws.com'
assert db_config['port'] == 8182
assert db_config['aoss_port'] == 443
assert db_config['region'] == 'us-east-1'
print('✓ All Neptune parameters validated correctly')
except Exception as e:
print(f'✗ Factory creation failed: {e}')
raise
print('✓ Neptune factory creation tests complete')
def test_neptune_factory_missing_credentials():
"""Test Neptune factory creation fails gracefully without AWS credentials."""
print('\nTesting Neptune factory behavior with missing AWS credentials...')
# Mock AWS session with no credentials
mock_session = MagicMock()
mock_session.get_credentials.return_value = None
mock_session.region_name = 'us-east-1'
test_config = {
'database': {
'provider': 'neptune',
'providers': {
'neptune': {
'host': 'neptune-db://test-cluster.us-east-1.neptune.amazonaws.com',
'aoss_host': 'test-aoss.us-east-1.aoss.amazonaws.com',
'port': 8182,
'aoss_port': 443,
}
},
}
}
with patch('boto3.Session', return_value=mock_session):
try:
DatabaseDriverFactory.create_config(test_config)
print('✗ Factory should have failed with missing credentials')
raise AssertionError('Expected ValueError for missing AWS credentials')
except ValueError as e:
print(f'✓ Missing credentials rejected: {str(e)[:60]}...')
assert 'AWS credentials not configured' in str(e)
assert 'aws configure' in str(e).lower()
print('✓ Missing credentials handling validated')
def test_neptune_factory_import_check():
"""Test Neptune factory import checking and error messages."""
print('\nTesting Neptune driver import availability...')
try:
from graphiti_core.driver.neptune_driver import NeptuneDriver
print('✓ NeptuneDriver successfully imported')
print(f' NeptuneDriver class: {NeptuneDriver.__name__}')
except ImportError as e:
print(f'⚠ NeptuneDriver not available: {e}')
print(' This is expected if graphiti-core[neptune] is not installed')
print(' Install with: uv add graphiti-core[neptune]')
print('✓ Import check complete')
def test_database_providers_config_with_neptune():
"""Test DatabaseProvidersConfig accepts Neptune configuration."""
print('\nTesting DatabaseProvidersConfig with Neptune...')
try:
config = DatabaseProvidersConfig(
neptune=NeptuneProviderConfig(
host='neptune-db://my-cluster.us-east-1.neptune.amazonaws.com',
aoss_host='my-aoss.us-east-1.aoss.amazonaws.com',
port=8182,
aoss_port=443,
region='us-east-1',
)
)
print('✓ DatabaseProvidersConfig created with Neptune')
assert config.neptune is not None
assert config.neptune.host == 'neptune-db://my-cluster.us-east-1.neptune.amazonaws.com'
assert config.neptune.aoss_host == 'my-aoss.us-east-1.aoss.amazonaws.com'
assert config.neptune.port == 8182
assert config.neptune.aoss_port == 443
assert config.neptune.region == 'us-east-1'
print('✓ All Neptune fields validated correctly')
except Exception as e:
print(f'✗ Failed to create DatabaseProvidersConfig with Neptune: {e}')
raise
print('✓ DatabaseProvidersConfig Neptune integration complete')
if __name__ == '__main__':
print('=' * 70)
print('Neptune Configuration Tests')
print('=' * 70)
try:
test_neptune_provider_config_validation()
test_neptune_environment_overrides()
test_database_providers_config_with_neptune()
test_neptune_factory_import_check()
test_neptune_factory_creation_with_mock_credentials()
test_neptune_factory_missing_credentials()
print('\n' + '=' * 70)
print('All Neptune tests passed!')
print('=' * 70)
except Exception as e:
print('\n' + '=' * 70)
print(f'Tests failed: {e}')
print('=' * 70)
raise

View file

@ -0,0 +1,225 @@
#!/usr/bin/env python3
"""
Simple Neptune validation tests that can run with minimal dependencies.
These tests verify core Neptune configuration logic without requiring
full MCP server dependencies.
"""
def test_neptune_endpoint_validation():
"""Test Neptune endpoint format validation logic."""
print('\nTesting Neptune endpoint format validation...')
valid_database_endpoints = [
'neptune-db://my-cluster.us-east-1.neptune.amazonaws.com',
'neptune-db://cluster.region.neptune.amazonaws.com',
'neptune-db://localhost',
]
valid_analytics_endpoints = [
'neptune-graph://g-abc123xyz',
'neptune-graph://g-12345',
]
invalid_endpoints = [
'https://neptune.com',
'http://localhost',
'neptune://wrong-prefix',
'bolt://localhost',
'redis://localhost',
]
# Test valid Database endpoints
for endpoint in valid_database_endpoints:
is_valid = endpoint.startswith('neptune-db://')
assert is_valid, f'Expected {endpoint} to be valid Database endpoint'
print(f'✓ Valid Database endpoint: {endpoint}')
# Test valid Analytics endpoints
for endpoint in valid_analytics_endpoints:
is_valid = endpoint.startswith('neptune-graph://')
assert is_valid, f'Expected {endpoint} to be valid Analytics endpoint'
print(f'✓ Valid Analytics endpoint: {endpoint}')
# Test invalid endpoints
for endpoint in invalid_endpoints:
is_valid = endpoint.startswith(('neptune-db://', 'neptune-graph://'))
assert not is_valid, f'Expected {endpoint} to be invalid'
print(f'✓ Rejected invalid endpoint: {endpoint}')
print('✓ Neptune endpoint validation logic verified')
def test_neptune_port_ranges():
"""Test Neptune port validation logic."""
print('\nTesting Neptune port validation...')
valid_ports = [1, 8182, 9999, 443, 65535]
invalid_ports = [0, -1, 70000, 100000]
# Test valid ports
for port in valid_ports:
is_valid = 1 <= port <= 65535
assert is_valid, f'Expected {port} to be valid'
print(f'✓ Valid port: {port}')
# Test invalid ports
for port in invalid_ports:
is_valid = 1 <= port <= 65535
assert not is_valid, f'Expected {port} to be invalid'
print(f'✓ Rejected invalid port: {port}')
print('✓ Neptune port validation logic verified')
def test_neptune_configuration_requirements():
"""Test Neptune configuration requirements."""
print('\nTesting Neptune configuration requirements...')
# Neptune requires both graph endpoint and AOSS endpoint
required_fields = ['host', 'aoss_host', 'port', 'aoss_port']
print('Neptune requires the following configuration fields:')
for field in required_fields:
print(f' - {field}')
# Verify required fields are documented
assert 'host' in required_fields
assert 'aoss_host' in required_fields
assert 'port' in required_fields
assert 'aoss_port' in required_fields
print('✓ All required Neptune fields documented')
# Verify default values are sensible
defaults = {
'host': 'neptune-db://localhost',
'port': 8182,
'aoss_port': 443,
}
print('\nDefault values:')
for field, value in defaults.items():
print(f' - {field}: {value}')
assert defaults['port'] == 8182 # Standard Neptune port
assert defaults['aoss_port'] == 443 # Standard HTTPS port for AOSS
print('✓ Neptune configuration requirements verified')
def test_neptune_error_messages():
"""Test Neptune error message quality."""
print('\nTesting Neptune error message quality...')
# Error messages should be helpful and actionable
error_scenarios = {
'invalid_endpoint': 'host must start with neptune-db:// or neptune-graph://',
'missing_aoss': 'requires aoss_host',
'missing_credentials': 'AWS credentials not configured',
'setup_help': 'aws configure',
}
print('Expected error message content:')
for scenario, expected in error_scenarios.items():
print(f' - {scenario}: "{expected}"')
# Verify error messages are informative
assert len(error_scenarios) > 0
assert all(len(msg) > 10 for msg in error_scenarios.values())
print('✓ Error message content verified')
def test_neptune_aws_integration():
"""Test Neptune AWS integration requirements."""
print('\nTesting Neptune AWS integration requirements...')
# Neptune requires AWS credentials
aws_credential_sources = [
'AWS CLI (aws configure)',
'Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)',
'IAM role (when running on AWS)',
'Credentials file (~/.aws/credentials)',
]
print('Neptune supports the following AWS credential sources:')
for source in aws_credential_sources:
print(f' - {source}')
assert len(aws_credential_sources) >= 4
print('✓ AWS credential sources documented')
# Neptune requires AWS region
print('\nNeptune requires AWS region configuration')
region_sources = [
'config.yaml (region field)',
'AWS_REGION environment variable',
'AWS profile default region',
]
for source in region_sources:
print(f' - {source}')
assert len(region_sources) >= 3
print('✓ Region configuration sources documented')
def test_neptune_dependencies():
"""Test Neptune dependency requirements."""
print('\nTesting Neptune dependency requirements...')
# Neptune requires specific Python packages
required_packages = [
'boto3>=1.39.16',
'opensearch-py>=3.0.0',
'langchain-aws>=0.2.29',
]
print('Neptune requires the following Python packages:')
for package in required_packages:
print(f' - {package}')
assert len(required_packages) >= 3
print('✓ Dependency requirements documented')
# Installation methods
install_commands = [
'pip install graphiti-core[neptune]',
'uv add graphiti-core[neptune]',
]
print('\nInstallation commands:')
for cmd in install_commands:
print(f' - {cmd}')
assert len(install_commands) >= 2
print('✓ Installation methods documented')
if __name__ == '__main__':
print('=' * 70)
print('Neptune Simple Validation Tests')
print('=' * 70)
try:
test_neptune_endpoint_validation()
test_neptune_port_ranges()
test_neptune_configuration_requirements()
test_neptune_error_messages()
test_neptune_aws_integration()
test_neptune_dependencies()
print('\n' + '=' * 70)
print('All simple validation tests passed!')
print('=' * 70)
print('\nNote: For full integration tests, run:')
print(' python tests/test_neptune_configuration.py')
print('=' * 70)
except AssertionError as e:
print('\n' + '=' * 70)
print(f'Test failed: {e}')
print('=' * 70)
raise

289
mcp_server/uv.lock generated
View file

@ -233,6 +233,34 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
]
[[package]]
name = "boto3"
version = "1.41.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
{ name = "jmespath" },
{ name = "s3transfer" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fa/81/2600e83ddd7cb1dac43d28fd39774434afcda0d85d730402192b1a9266a3/boto3-1.41.2.tar.gz", hash = "sha256:7054fbc61cadab383f40ea6d725013ba6c8f569641dddb14c0055e790280ad6c", size = 111593, upload-time = "2025-11-21T20:32:08.622Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/41/1ed7fdc3f124c1cf2df78e605588fa78a182410b832f5b71944a69436171/boto3-1.41.2-py3-none-any.whl", hash = "sha256:edcde82fdae4201aa690e3683f8e5b1a846cf1bbf79d03db4fa8a2f6f46dba9c", size = 139343, upload-time = "2025-11-21T20:32:07.147Z" },
]
[[package]]
name = "botocore"
version = "1.41.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "python-dateutil" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/0b/6eb5dc752b240dd0b76d7e3257ae25b70683896d1789e7bfb78fba7c7c99/botocore-1.41.2.tar.gz", hash = "sha256:49a3e8f4c1a1759a687941fef8b36efd7bafcf63c1ef74aa75d6497eb4887c9c", size = 14660558, upload-time = "2025-11-21T20:31:58.785Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/4d/516ee2157c0686fbe48ca8b94dffc17a0c35040d4626761d74b1a43215c8/botocore-1.41.2-py3-none-any.whl", hash = "sha256:154052dfaa7292212f01c8fab822c76cd10a15a7e164e4c45e4634eb40214b90", size = 14324839, upload-time = "2025-11-21T20:31:56.236Z" },
]
[[package]]
name = "cachetools"
version = "5.5.2"
@ -455,6 +483,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
]
[[package]]
name = "events"
version = "0.5"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/25/ed/e47dec0626edd468c84c04d97769e7ab4ea6457b7f54dcb3f72b17fcd876/Events-0.5-py3-none-any.whl", hash = "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd", size = 6758, upload-time = "2023-07-31T08:23:13.645Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.0"
@ -648,7 +684,7 @@ wheels = [
[[package]]
name = "graphiti-core"
version = "0.23.1"
version = "0.24.2"
source = { editable = "../" }
dependencies = [
{ name = "diskcache" },
@ -665,6 +701,12 @@ dependencies = [
falkordb = [
{ name = "falkordb" },
]
neptune = [
{ name = "boto3" },
{ name = "langchain-aws", version = "0.2.35", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "langchain-aws", version = "1.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "opensearch-py" },
]
[package.metadata]
requires-dist = [
@ -734,6 +776,67 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/f8/14672d69a91495f43462c5490067eeafc30346e81bda1a62848e897f9bc3/groq-0.31.0-py3-none-any.whl", hash = "sha256:5e3c7ec9728b7cccf913da982a9b5ebb46dc18a070b35e12a3d6a1e12d6b0f7f", size = 131365, upload-time = "2025-08-05T23:13:59.768Z" },
]
[[package]]
name = "grpcio"
version = "1.76.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/17/ff4795dc9a34b6aee6ec379f1b66438a3789cd1315aac0cbab60d92f74b3/grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc", size = 5840037, upload-time = "2025-10-21T16:20:25.069Z" },
{ url = "https://files.pythonhosted.org/packages/4e/ff/35f9b96e3fa2f12e1dcd58a4513a2e2294a001d64dec81677361b7040c9a/grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde", size = 11836482, upload-time = "2025-10-21T16:20:30.113Z" },
{ url = "https://files.pythonhosted.org/packages/3e/1c/8374990f9545e99462caacea5413ed783014b3b66ace49e35c533f07507b/grpcio-1.76.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:035d90bc79eaa4bed83f524331d55e35820725c9fbb00ffa1904d5550ed7ede3", size = 6407178, upload-time = "2025-10-21T16:20:32.733Z" },
{ url = "https://files.pythonhosted.org/packages/1e/77/36fd7d7c75a6c12542c90a6d647a27935a1ecaad03e0ffdb7c42db6b04d2/grpcio-1.76.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4215d3a102bd95e2e11b5395c78562967959824156af11fa93d18fdd18050990", size = 7075684, upload-time = "2025-10-21T16:20:35.435Z" },
{ url = "https://files.pythonhosted.org/packages/38/f7/e3cdb252492278e004722306c5a8935eae91e64ea11f0af3437a7de2e2b7/grpcio-1.76.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49ce47231818806067aea3324d4bf13825b658ad662d3b25fada0bdad9b8a6af", size = 6611133, upload-time = "2025-10-21T16:20:37.541Z" },
{ url = "https://files.pythonhosted.org/packages/7e/20/340db7af162ccd20a0893b5f3c4a5d676af7b71105517e62279b5b61d95a/grpcio-1.76.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8cc3309d8e08fd79089e13ed4819d0af72aa935dd8f435a195fd152796752ff2", size = 7195507, upload-time = "2025-10-21T16:20:39.643Z" },
{ url = "https://files.pythonhosted.org/packages/10/f0/b2160addc1487bd8fa4810857a27132fb4ce35c1b330c2f3ac45d697b106/grpcio-1.76.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:971fd5a1d6e62e00d945423a567e42eb1fa678ba89072832185ca836a94daaa6", size = 8160651, upload-time = "2025-10-21T16:20:42.492Z" },
{ url = "https://files.pythonhosted.org/packages/2c/2c/ac6f98aa113c6ef111b3f347854e99ebb7fb9d8f7bb3af1491d438f62af4/grpcio-1.76.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d9adda641db7207e800a7f089068f6f645959f2df27e870ee81d44701dd9db3", size = 7620568, upload-time = "2025-10-21T16:20:45.995Z" },
{ url = "https://files.pythonhosted.org/packages/90/84/7852f7e087285e3ac17a2703bc4129fafee52d77c6c82af97d905566857e/grpcio-1.76.0-cp310-cp310-win32.whl", hash = "sha256:063065249d9e7e0782d03d2bca50787f53bd0fb89a67de9a7b521c4a01f1989b", size = 3998879, upload-time = "2025-10-21T16:20:48.592Z" },
{ url = "https://files.pythonhosted.org/packages/10/30/d3d2adcbb6dd3ff59d6ac3df6ef830e02b437fb5c90990429fd180e52f30/grpcio-1.76.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6ae758eb08088d36812dd5d9af7a9859c05b1e0f714470ea243694b49278e7b", size = 4706892, upload-time = "2025-10-21T16:20:50.697Z" },
{ url = "https://files.pythonhosted.org/packages/a0/00/8163a1beeb6971f66b4bbe6ac9457b97948beba8dd2fc8e1281dce7f79ec/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a", size = 5843567, upload-time = "2025-10-21T16:20:52.829Z" },
{ url = "https://files.pythonhosted.org/packages/10/c1/934202f5cf335e6d852530ce14ddb0fef21be612ba9ecbbcbd4d748ca32d/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c", size = 11848017, upload-time = "2025-10-21T16:20:56.705Z" },
{ url = "https://files.pythonhosted.org/packages/11/0b/8dec16b1863d74af6eb3543928600ec2195af49ca58b16334972f6775663/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465", size = 6412027, upload-time = "2025-10-21T16:20:59.3Z" },
{ url = "https://files.pythonhosted.org/packages/d7/64/7b9e6e7ab910bea9d46f2c090380bab274a0b91fb0a2fe9b0cd399fffa12/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48", size = 7075913, upload-time = "2025-10-21T16:21:01.645Z" },
{ url = "https://files.pythonhosted.org/packages/68/86/093c46e9546073cefa789bd76d44c5cb2abc824ca62af0c18be590ff13ba/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da", size = 6615417, upload-time = "2025-10-21T16:21:03.844Z" },
{ url = "https://files.pythonhosted.org/packages/f7/b6/5709a3a68500a9c03da6fb71740dcdd5ef245e39266461a03f31a57036d8/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397", size = 7199683, upload-time = "2025-10-21T16:21:06.195Z" },
{ url = "https://files.pythonhosted.org/packages/91/d3/4b1f2bf16ed52ce0b508161df3a2d186e4935379a159a834cb4a7d687429/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749", size = 8163109, upload-time = "2025-10-21T16:21:08.498Z" },
{ url = "https://files.pythonhosted.org/packages/5c/61/d9043f95f5f4cf085ac5dd6137b469d41befb04bd80280952ffa2a4c3f12/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00", size = 7626676, upload-time = "2025-10-21T16:21:10.693Z" },
{ url = "https://files.pythonhosted.org/packages/36/95/fd9a5152ca02d8881e4dd419cdd790e11805979f499a2e5b96488b85cf27/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054", size = 3997688, upload-time = "2025-10-21T16:21:12.746Z" },
{ url = "https://files.pythonhosted.org/packages/60/9c/5c359c8d4c9176cfa3c61ecd4efe5affe1f38d9bae81e81ac7186b4c9cc8/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d", size = 4709315, upload-time = "2025-10-21T16:21:15.26Z" },
{ url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" },
{ url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" },
{ url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" },
{ url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" },
{ url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" },
{ url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" },
{ url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" },
{ url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" },
{ url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" },
{ url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" },
{ url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" },
{ url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" },
{ url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" },
{ url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" },
{ url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" },
{ url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" },
{ url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" },
{ url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" },
{ url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" },
{ url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" },
{ url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" },
{ url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" },
{ url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" },
{ url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" },
{ url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" },
{ url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" },
{ url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" },
{ url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" },
{ url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" },
{ url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" },
]
[[package]]
name = "h11"
version = "0.16.0"
@ -916,6 +1019,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" },
]
[[package]]
name = "jmespath"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" },
]
[[package]]
name = "joblib"
version = "1.5.1"
@ -947,35 +1059,119 @@ wheels = [
]
[[package]]
name = "langchain-core"
version = "0.3.74"
name = "langchain-aws"
version = "0.2.35"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsonpatch" },
{ name = "langsmith" },
{ name = "packaging" },
{ name = "pydantic" },
{ name = "pyyaml" },
{ name = "tenacity" },
{ name = "typing-extensions" },
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version == '3.12.*'",
"python_full_version == '3.13.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/f1/c6/5d755a0f1f4857abbe5ea6f5907ed0e2b5df52bf4dde0a0fd768290e3084/langchain_core-0.3.74.tar.gz", hash = "sha256:ff604441aeade942fbcc0a3860a592daba7671345230c2078ba2eb5f82b6ba76", size = 569553, upload-time = "2025-08-07T20:47:05.094Z" }
dependencies = [
{ name = "boto3", marker = "python_full_version >= '3.12'" },
{ name = "langchain-core", version = "0.3.80", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "numpy", marker = "python_full_version >= '3.12'" },
{ name = "pydantic", marker = "python_full_version >= '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8d/7a/19a903725acbb1c4481dc0391b2551250bf4e04cbe5a891a55e09319772b/langchain_aws-0.2.35.tar.gz", hash = "sha256:45793a34fe45d365f4292cc768db74669ca24601d2c5da1ac6f44403750d70af", size = 120567, upload-time = "2025-10-02T23:59:57.204Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/26/545283681ac0379d31c7ad0bac5f195e1982092d76c65ca048db9e3cec0e/langchain_core-0.3.74-py3-none-any.whl", hash = "sha256:088338b5bc2f6a66892f9afc777992c24ee3188f41cbc603d09181e34a228ce7", size = 443453, upload-time = "2025-08-07T20:47:03.853Z" },
{ url = "https://files.pythonhosted.org/packages/41/92/1827652b4ed6d8ffaffe8b40be49a6889a9b3cb4b523fb56871691c48601/langchain_aws-0.2.35-py3-none-any.whl", hash = "sha256:8ddb10f3c29f6d52bcbaa4d7f4f56462acf01f608adc7c70f41e5a476899a6bc", size = 145620, upload-time = "2025-10-02T23:59:55.288Z" },
]
[[package]]
name = "langchain-aws"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.12'",
]
dependencies = [
{ name = "boto3", marker = "python_full_version < '3.12'" },
{ name = "langchain-core", version = "1.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", marker = "python_full_version < '3.12'" },
{ name = "pydantic", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/1d/bb306951b1c394b7a27effb8eb6c9ee65dd77fcc4be7c20f76e3299a9e1e/langchain_aws-1.1.0.tar.gz", hash = "sha256:1e2f8570328eae4907c3cf7e900dc68d8034ddc865d9dc96823c9f9d8cccb901", size = 393899, upload-time = "2025-11-24T14:35:24.216Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/33/91b8d2a7570657b371382b45054142c54165a51706990a5c1b4cc40c0e9a/langchain_aws-1.1.0-py3-none-any.whl", hash = "sha256:8ec074615b42839e035354063717374c32c63f5028ef5221ba073fd5f3ef5e37", size = 152432, upload-time = "2025-11-24T14:35:23.004Z" },
]
[[package]]
name = "langchain-core"
version = "0.3.80"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version == '3.12.*'",
"python_full_version == '3.13.*'",
]
dependencies = [
{ name = "jsonpatch", marker = "python_full_version >= '3.12'" },
{ name = "langsmith", marker = "python_full_version >= '3.12'" },
{ name = "packaging", marker = "python_full_version >= '3.12'" },
{ name = "pydantic", marker = "python_full_version >= '3.12'" },
{ name = "pyyaml", marker = "python_full_version >= '3.12'" },
{ name = "tenacity", marker = "python_full_version >= '3.12'" },
{ name = "typing-extensions", marker = "python_full_version >= '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/49/49/f76647b7ba1a6f9c11b0343056ab4d3e5fc445981d205237fed882b2ad60/langchain_core-0.3.80.tar.gz", hash = "sha256:29636b82513ab49e834764d023c4d18554d3d719a185d37b019d0a8ae948c6bb", size = 583629, upload-time = "2025-11-19T22:23:18.771Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/da/e8/e7a090ebe37f2b071c64e81b99fb1273b3151ae932f560bb94c22f191cde/langchain_core-0.3.80-py3-none-any.whl", hash = "sha256:2141e3838d100d17dce2359f561ec0df52c526bae0de6d4f469f8026c5747456", size = 450786, upload-time = "2025-11-19T22:23:17.133Z" },
]
[[package]]
name = "langchain-core"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.12'",
]
dependencies = [
{ name = "jsonpatch", marker = "python_full_version < '3.12'" },
{ name = "langsmith", marker = "python_full_version < '3.12'" },
{ name = "packaging", marker = "python_full_version < '3.12'" },
{ name = "pydantic", marker = "python_full_version < '3.12'" },
{ name = "pyyaml", marker = "python_full_version < '3.12'" },
{ name = "tenacity", marker = "python_full_version < '3.12'" },
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1e/17/67c1cc2ace919e2b02dd9d783154d7fb3f1495a4ef835d9cd163b7855ac2/langchain_core-1.1.0.tar.gz", hash = "sha256:2b76a82d427922c8bc51c08404af4fc2a29e9f161dfe2297cb05091e810201e7", size = 781995, upload-time = "2025-11-21T21:01:26.958Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/71/1e/e129fc471a2d2a7b3804480a937b5ab9319cab9f4142624fcb115f925501/langchain_core-1.1.0-py3-none-any.whl", hash = "sha256:2c9f27dadc6d21ed4aa46506a37a56e6a7e2d2f9141922dc5c251ba921822ee6", size = 473752, upload-time = "2025-11-21T21:01:25.841Z" },
]
[[package]]
name = "langchain-text-splitters"
version = "0.3.9"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version == '3.12.*'",
"python_full_version == '3.13.*'",
]
dependencies = [
{ name = "langchain-core" },
{ name = "langchain-core", version = "0.3.80", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/91/52/d43ad77acae169210cc476cbc1e4ab37a701017c950211a11ab500fe7d7e/langchain_text_splitters-0.3.9.tar.gz", hash = "sha256:7cd1e5a3aaf609979583eeca2eb34177622570b8fa8f586a605c6b1c34e7ebdb", size = 45260, upload-time = "2025-07-24T14:38:45.14Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/52/7638394b88bc15083fd2c3752a843784d9d2d110d68fed6437c8607fb749/langchain_text_splitters-0.3.9-py3-none-any.whl", hash = "sha256:cee0bb816211584ea79cc79927317c358543f40404bcfdd69e69ba3ccde54401", size = 33314, upload-time = "2025-07-24T14:38:43.953Z" },
]
[[package]]
name = "langchain-text-splitters"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.12'",
]
dependencies = [
{ name = "langchain-core", version = "1.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fa/2e/c833dcc379c1c086453708ef5eef7d4d1f808559ca4458bd6569d5d83ad7/langchain_text_splitters-1.0.0.tar.gz", hash = "sha256:d8580a20ad7ed10b432feb273e5758b2cc0902d094919629cec0e1ad691a6744", size = 264257, upload-time = "2025-10-17T14:33:41.743Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/97/d362353ab04f865af6f81d4d46e7aa428734aa032de0017934b771fc34b7/langchain_text_splitters-1.0.0-py3-none-any.whl", hash = "sha256:f00c8219d3468f2c5bd951b708b6a7dd9bc3c62d0cfb83124c377f7170f33b2e", size = 33851, upload-time = "2025-10-17T14:33:40.46Z" },
]
[[package]]
name = "langsmith"
version = "0.4.16"
@ -1074,10 +1270,10 @@ wheels = [
[[package]]
name = "mcp-server"
version = "1.0.1"
version = "1.0.2"
source = { virtual = "." }
dependencies = [
{ name = "graphiti-core", extra = ["falkordb"] },
{ name = "graphiti-core", extra = ["falkordb", "neptune"] },
{ name = "mcp" },
{ name = "openai" },
{ name = "pydantic-settings" },
@ -1115,7 +1311,7 @@ requires-dist = [
{ name = "anthropic", marker = "extra == 'providers'", specifier = ">=0.49.0" },
{ name = "azure-identity", marker = "extra == 'azure'", specifier = ">=1.21.0" },
{ name = "google-genai", marker = "extra == 'providers'", specifier = ">=1.8.0" },
{ name = "graphiti-core", extras = ["falkordb"], editable = "../" },
{ name = "graphiti-core", extras = ["falkordb", "neptune"], editable = "../" },
{ name = "groq", marker = "extra == 'providers'", specifier = ">=0.2.0" },
{ name = "mcp", specifier = ">=1.9.4" },
{ name = "openai", specifier = ">=1.91.0" },
@ -1514,6 +1710,35 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/d2/f99bdd6fc737d6b3cf0df895508d621fc9a386b375a1230ee81d46c5436e/openai-1.91.0-py3-none-any.whl", hash = "sha256:207f87aa3bc49365e014fac2f7e291b99929f4fe126c4654143440e0ad446a5f", size = 735837, upload-time = "2025-06-23T18:27:08.913Z" },
]
[[package]]
name = "opensearch-protobufs"
version = "0.19.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "grpcio" },
{ name = "protobuf" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/16/e2/8a09dbdbfe51e30dfecb625a0f5c524a53bfa4b1fba168f73ac85621dba2/opensearch_protobufs-0.19.0-py3-none-any.whl", hash = "sha256:5137c9c2323cc7debb694754b820ca4cfb5fc8eb180c41ff125698c3ee11bfc2", size = 39778, upload-time = "2025-09-29T20:05:52.379Z" },
]
[[package]]
name = "opensearch-py"
version = "3.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "events" },
{ name = "opensearch-protobufs" },
{ name = "python-dateutil" },
{ name = "requests" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/65/9f/d4969f7e8fa221bfebf254cc3056e7c743ce36ac9874e06110474f7c947d/opensearch_py-3.1.0.tar.gz", hash = "sha256:883573af13175ff102b61c80b77934a9e937bdcc40cda2b92051ad53336bc055", size = 258616, upload-time = "2025-11-20T16:37:36.777Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/a1/293c8ad81768ad625283d960685bde07c6302abf20a685e693b48ab6eb91/opensearch_py-3.1.0-py3-none-any.whl", hash = "sha256:e5af83d0454323e6ea9ddee8c0dcc185c0181054592d23cb701da46271a3b65b", size = 385729, upload-time = "2025-11-20T16:37:34.941Z" },
]
[[package]]
name = "orjson"
version = "3.11.2"
@ -1817,6 +2042,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" },
]
[[package]]
name = "protobuf"
version = "6.33.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0a/03/a1440979a3f74f16cab3b75b0da1a1a7f922d56a8ddea96092391998edc0/protobuf-6.33.1.tar.gz", hash = "sha256:97f65757e8d09870de6fd973aeddb92f85435607235d20b2dfed93405d00c85b", size = 443432, upload-time = "2025-11-13T16:44:18.895Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/06/f1/446a9bbd2c60772ca36556bac8bfde40eceb28d9cc7838755bc41e001d8f/protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b", size = 425593, upload-time = "2025-11-13T16:44:06.275Z" },
{ url = "https://files.pythonhosted.org/packages/a6/79/8780a378c650e3df849b73de8b13cf5412f521ca2ff9b78a45c247029440/protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed", size = 436883, upload-time = "2025-11-13T16:44:09.222Z" },
{ url = "https://files.pythonhosted.org/packages/cd/93/26213ff72b103ae55bb0d73e7fb91ea570ef407c3ab4fd2f1f27cac16044/protobuf-6.33.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:fe34575f2bdde76ac429ec7b570235bf0c788883e70aee90068e9981806f2490", size = 427522, upload-time = "2025-11-13T16:44:10.475Z" },
{ url = "https://files.pythonhosted.org/packages/c2/32/df4a35247923393aa6b887c3b3244a8c941c32a25681775f96e2b418f90e/protobuf-6.33.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:f8adba2e44cde2d7618996b3fc02341f03f5bc3f2748be72dc7b063319276178", size = 324445, upload-time = "2025-11-13T16:44:11.869Z" },
{ url = "https://files.pythonhosted.org/packages/8e/d0/d796e419e2ec93d2f3fa44888861c3f88f722cde02b7c3488fcc6a166820/protobuf-6.33.1-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:0f4cf01222c0d959c2b399142deb526de420be8236f22c71356e2a544e153c53", size = 339161, upload-time = "2025-11-13T16:44:12.778Z" },
{ url = "https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1", size = 323171, upload-time = "2025-11-13T16:44:14.035Z" },
{ url = "https://files.pythonhosted.org/packages/08/b4/46310463b4f6ceef310f8348786f3cff181cea671578e3d9743ba61a459e/protobuf-6.33.1-py3-none-any.whl", hash = "sha256:d595a9fd694fdeb061a62fbe10eb039cc1e444df81ec9bb70c7fc59ebcb1eafa", size = 170477, upload-time = "2025-11-13T16:44:17.633Z" },
]
[[package]]
name = "psutil"
version = "7.1.2"
@ -2321,6 +2561,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" },
]
[[package]]
name = "s3transfer"
version = "0.15.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ca/bb/940d6af975948c1cc18f44545ffb219d3c35d78ec972b42ae229e8e37e08/s3transfer-0.15.0.tar.gz", hash = "sha256:d36fac8d0e3603eff9b5bfa4282c7ce6feb0301a633566153cbd0b93d11d8379", size = 152185, upload-time = "2025-11-20T20:28:56.327Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5f/e1/5ef25f52973aa12a19cf4e1375d00932d7fb354ffd310487ba7d44225c1a/s3transfer-0.15.0-py3-none-any.whl", hash = "sha256:6f8bf5caa31a0865c4081186689db1b2534cef721d104eb26101de4b9d6a5852", size = 85984, upload-time = "2025-11-20T20:28:55.046Z" },
]
[[package]]
name = "safetensors"
version = "0.6.2"
@ -2762,7 +3014,8 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
{ name = "aiolimiter" },
{ name = "langchain-text-splitters" },
{ name = "langchain-text-splitters", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "langchain-text-splitters", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", marker = "python_full_version < '3.14'" },
{ name = "pillow" },
{ name = "pydantic" },

View file

@ -1,7 +1,7 @@
[project]
name = "graphiti-core"
description = "A temporal graph building library"
version = "0.24.3"
version = "0.24.4"
authors = [
{ name = "Paul Paliychuk", email = "paul@getzep.com" },
{ name = "Preston Rasmussen", email = "preston@getzep.com" },

2
uv.lock generated
View file

@ -808,7 +808,7 @@ wheels = [
[[package]]
name = "graphiti-core"
version = "0.24.3"
version = "0.24.4"
source = { editable = "." }
dependencies = [
{ name = "diskcache" },