Compare commits

...

27 commits

Author SHA1 Message Date
032c2a20c6 Update mcp_server/.env
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Lint with Ruff / ruff (push) Has been cancelled
Pyright Type Check / pyright (push) Has been cancelled
Tests / unit-tests (push) Has been cancelled
Tests / database-integration-tests (push) Has been cancelled
2025-12-15 13:55:37 +00:00
323bd57c9b Update mcp_server/docker/docker-compose-neo4j.yml
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-15 13:54:43 +00:00
8bbf068eae Update mcp_server/docker/docker-compose-neo4j.yml
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:59:39 +00:00
f3132dc763 Update mcp_server/docker/docker-compose-neo4j.yml
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:53:20 +00:00
efd21d6c7e Update mcp_server/docker/docker-compose-neo4j.yml
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:48:10 +00:00
8f4c1ea8b9 Update mcp_server/docker/docker-compose-neo4j.yml
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:41:34 +00:00
59802cfae5 Update mcp_server/.env
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
Tests / unit-tests (push) Waiting to run
2025-12-14 19:41:01 +00:00
b5a8227035 Update graphiti_core/driver/neo4j_driver.py
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:23:08 +00:00
becd9107f9 Update mcp_server/.env
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:21:10 +00:00
6a5e4a356e Update mcp_server/.env
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:20:37 +00:00
c5d7597319 Add mcp_server/.env
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Lint with Ruff / ruff (push) Waiting to run
Pyright Type Check / pyright (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Tests / database-integration-tests (push) Waiting to run
2025-12-14 19:10:44 +00:00
Daniel Chalef
d6ff7bb78c @husniadil has signed the CLA in getzep/graphiti#1105 2025-12-13 19:38:11 -08:00
Daniel Chalef
5e593dd096 @NShumway has signed the CLA in getzep/graphiti#1102 2025-12-09 17:26:59 -08:00
Ronald Mego Solano
1de752646a
Fix/model name config (#1094)
fix: Replace non-existent model names and handle Neo4j index race condition

- Replace hardcoded non-existent models (gpt-5-mini, gpt-5-nano, gpt-4.1)
  with valid gpt-4o-mini across config files and default client
- Add exception handling for EquivalentSchemaRuleAlreadyExists in
  Neo4j index creation to prevent race condition failures on startup
2025-12-08 11:31:04 -05:00
Preston Rasmussen
24105b9556
add property filters (#1099)
* add property filters

* update
2025-12-08 10:50:19 -05:00
Daniel Chalef
2d50fa565d @ronaldmego has signed the CLA in getzep/graphiti#1094 2025-12-05 06:59:53 -08:00
Preston Rasmussen
6dc7b8800b
bump (#1093)
update
2025-12-04 13:11:33 -05:00
Preston Rasmussen
2d262cf22d
foreign language fix (#1090) 2025-12-03 11:47:12 -05:00
Daniel Chalef
bddd469167
Disable issue triage and daily maintenance workflows (#1089)
Remove automated issue triage and duplicate detection workflows to reduce
automated issue management.

- Remove .github/workflows/issue-triage.yml
- Remove .github/workflows/daily_issue_maintenance.yml

🤖 Generated with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 15:50:07 -08:00
Daniel Chalef
422558d06c @ZLBillShaw has signed the CLA in getzep/graphiti#1085 2025-11-26 18:46:03 -08:00
Daniel Chalef
3ce3962e4b @apetti1920 has signed the CLA in getzep/graphiti#1084 2025-11-24 13:07:46 -08:00
Daniel Chalef
352977a002 @donbr has signed the CLA in getzep/graphiti#1081 2025-11-23 21:19:55 -08:00
Daniel Chalef
582e093582 @supmo668 has signed the CLA in getzep/graphiti#1072 2025-11-23 17:13:50 -08:00
Daniel Chalef
ae78828f9c
fix: replace deprecated gemini-2.5-flash-lite-preview with gemini-2.5-flash-lite (#1076)
fix: replace deprecated gemini-2.5-flash-lite-preview-06-17 with gemini-2.5-flash-lite

Updated all references to the deprecated Gemini model in:
- graphiti_core/llm_client/gemini_client.py
- graphiti_core/cross_encoder/gemini_reranker_client.py
- tests/llm_client/test_gemini_client.py
- README.md

This resolves 404 errors when using Gemini clients.

Fixes #1075

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Daniel Chalef <danielchalef@users.noreply.github.com>
2025-11-20 16:03:51 -08:00
Preston Rasmussen
d2654003ff
update summary character limit (#1073)
* update

* update tests
2025-11-18 17:16:02 -05:00
Daniel Chalef
8b7ad6f84c
Update default Anthropic model to claude-haiku-4-5 (#1070)
* Update default Anthropic model to claude-haiku-4-5-latest

- Add Claude 4.5 models to AnthropicModel type (claude-sonnet-4-5-latest, claude-sonnet-4-5-20250929, claude-haiku-4-5-latest)
- Change DEFAULT_MODEL from claude-3-7-sonnet-latest to claude-haiku-4-5-latest
- Update test assertions to reflect new default model

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add Claude 4.5 models to max_tokens mapping

- Add claude-sonnet-4-5-latest, claude-sonnet-4-5-20250929, and claude-haiku-4-5-latest to ANTHROPIC_MODEL_MAX_TOKENS
- All Claude 4.5 models support 64K (65536) max output tokens
- Based on official Anthropic documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update uv.lock dependencies

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-15 09:07:18 -08:00
Didier Durand
de382071d1
[Doc]: fixing typos in various files (#1067) 2025-11-15 08:58:49 -08:00
31 changed files with 280 additions and 522 deletions

View file

@ -1,123 +0,0 @@
name: Daily Issue Maintenance
on:
schedule:
- cron: "0 0 * * *" # Every day at midnight
workflow_dispatch: # Manual trigger option
jobs:
find-legacy-duplicates:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch'
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
REPO: ${{ github.repository }}
Find potential duplicate issues in the repository:
1. Use `gh issue list --state open --limit 1000 --json number,title,body,createdAt` to get all open issues
2. For each issue, search for potential duplicates using `gh search issues` with keywords from the title and body
3. Compare issues to identify true duplicates using these criteria:
- Same bug or error being reported
- Same feature request (even if worded differently)
- Same question being asked
- Issues describing the same root problem
For each duplicate found:
- Add a comment linking to the original issue
- Apply the "duplicate" label using `gh issue edit`
- Be polite and explain why it's a duplicate
Focus on finding true duplicates, not just similar issues.
claude_args: |
--allowedTools "Bash(gh issue:*),Bash(gh search:*)"
--model claude-sonnet-4-5-20250929
check-stale-issues:
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
REPO: ${{ github.repository }}
Review stale issues and request confirmation:
1. Use `gh issue list --state open --limit 1000 --json number,title,updatedAt,comments` to get all open issues
2. Identify issues that are:
- Older than 60 days (based on updatedAt)
- Have no comments with "stale-check" label
- Are not labeled as "enhancement" or "documentation"
3. For each stale issue:
- Add a polite comment asking the issue originator if this is still relevant
- Apply a "stale-check" label to track that we've asked
- Use format: "@{author} Is this still an issue? Please confirm within 14 days or this issue will be closed."
Use:
- `gh issue view` to check issue details and labels
- `gh issue comment` to add comments
- `gh issue edit` to add the "stale-check" label
claude_args: |
--allowedTools "Bash(gh issue:*)"
--model claude-sonnet-4-5-20250929
close-unconfirmed-issues:
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
needs: check-stale-issues
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
REPO: ${{ github.repository }}
Close unconfirmed stale issues:
1. Use `gh issue list --state open --label "stale-check" --limit 1000 --json number,title,comments,updatedAt` to get issues with stale-check label
2. For each issue, check if:
- The "stale-check" comment was added 14+ days ago
- There has been no response from the issue author or activity since the comment
3. For issues meeting the criteria:
- Add a polite closing comment
- Close the issue using `gh issue close`
- Use format: "Closing due to inactivity. Feel free to reopen if this is still relevant."
Use:
- `gh issue view` to check issue comments and activity
- `gh issue comment` to add closing comment
- `gh issue close` to close the issue
claude_args: |
--allowedTools "Bash(gh issue:*)"
--model claude-sonnet-4-5-20250929

View file

@ -1,141 +0,0 @@
name: Issue Triage and Deduplication
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code for Issue Triage
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
allowed_non_write_users: "*"
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: |
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels. DO NOT check for duplicates - that's handled by a separate job.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
TASK OVERVIEW:
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
2. Next, use gh commands to get context about the issue:
- Use `gh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details
- Use `gh search issues` to find similar issues that might provide context for proper categorization
- You have access to these Bash commands:
- Bash(gh label list:*) - to get available labels
- Bash(gh issue view:*) - to view issue details
- Bash(gh issue edit:*) - to apply labels to the issue
- Bash(gh search:*) - to search for similar issues
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Database mentions (neo4j, falkordb, neptune, etc.)
- LLM providers mentioned (openai, anthropic, gemini, groq, etc.)
- Components affected (embeddings, search, prompts, server, mcp, etc.)
4. Select appropriate labels from the available labels list:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Add database-specific labels if mentioned: neo4j, falkordb, neptune
- Add component labels if applicable
- DO NOT add priority labels (P1, P2, P3)
- DO NOT add duplicate label - that's handled by the deduplication job
5. Apply the selected labels:
- Use `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2,label3"` to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using gh issue edit
- It's okay to not add any labels if none are clearly applicable
- DO NOT check for duplicates
claude_args: |
--allowedTools "Bash(gh label list:*),Bash(gh issue view:*),Bash(gh issue edit:*),Bash(gh search:*)"
--model claude-sonnet-4-5-20250929
deduplicate:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: triage
permissions:
contents: read
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Check for duplicate issues
uses: anthropics/claude-code-action@v1
with:
allowed_non_write_users: "*"
prompt: |
Analyze this new issue and check if it's a duplicate of existing issues in the repository.
Issue: #${{ github.event.issue.number }}
Repository: ${{ github.repository }}
Your task:
1. Use mcp__github__get_issue to get details of the current issue (#${{ github.event.issue.number }})
2. Search for similar existing OPEN issues using mcp__github__search_issues with relevant keywords from the issue title and body
3. Compare the new issue with existing ones to identify potential duplicates
Criteria for duplicates:
- Same bug or error being reported
- Same feature request (even if worded differently)
- Same question being asked
- Issues describing the same root problem
If you find duplicates:
- Add a comment on the new issue linking to the original issue(s)
- Apply the "duplicate" label to the new issue
- Be polite and explain why it's a duplicate
- Suggest the user follow the original issue for updates
If it's NOT a duplicate:
- Don't add any comments
- Don't modify labels
Use these tools:
- mcp__github__get_issue: Get issue details
- mcp__github__search_issues: Search for similar issues (use state:open)
- mcp__github__list_issues: List recent issues if needed
- mcp__github__create_issue_comment: Add a comment if duplicate found
- mcp__github__update_issue: Add "duplicate" label
Be thorough but efficient. Focus on finding true duplicates, not just similar issues.
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--allowedTools "mcp__github__get_issue,mcp__github__search_issues,mcp__github__list_issues,mcp__github__create_issue_comment,mcp__github__update_issue,mcp__github__get_issue_comments"
--model claude-sonnet-4-5-20250929

View file

@ -479,7 +479,7 @@ graphiti = Graphiti(
cross_encoder=GeminiRerankerClient(
config=LLMConfig(
api_key=api_key,
model="gemini-2.5-flash-lite-preview-06-17"
model="gemini-2.5-flash-lite"
)
)
)
@ -487,7 +487,7 @@ graphiti = Graphiti(
# Now you can use Graphiti with Google Gemini for all components
```
The Gemini reranker uses the `gemini-2.5-flash-lite-preview-06-17` model by default, which is optimized for
The Gemini reranker uses the `gemini-2.5-flash-lite` model by default, which is optimized for
cost-effective and low-latency classification tasks. It uses the same boolean classification approach as the OpenAI
reranker, leveraging Gemini's log probabilities feature to rank passage relevance.

View file

@ -34,7 +34,7 @@ You are not expected to provide support for Your Contributions, except to the ex
## Third-Party Submissions
Should You wish to submit work that is not Your original creation, You may submit it to Zep separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
Should You wish to submit work that is not Your original creation, You may submit it to Zep separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third party: [named here]".
## Notifications

View file

@ -40,8 +40,8 @@ from graphiti_core.nodes import EpisodeType
# Configure logging
logging.basicConfig(
level=INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
logger = logging.getLogger(__name__)
@ -49,20 +49,20 @@ load_dotenv()
# Neo4j connection parameters
# Make sure Neo4j Desktop is running with a local DBMS started
neo4j_uri = os.environ.get("NEO4J_URI", "bolt://localhost:7687")
neo4j_user = os.environ.get("NEO4J_USER", "neo4j")
neo4j_password = os.environ.get("NEO4J_PASSWORD", "password")
neo4j_uri = os.environ.get('NEO4J_URI', 'bolt://localhost:7687')
neo4j_user = os.environ.get('NEO4J_USER', 'neo4j')
neo4j_password = os.environ.get('NEO4J_PASSWORD', 'password')
# Azure OpenAI connection parameters
azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
azure_api_key = os.environ.get("AZURE_OPENAI_API_KEY")
azure_deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT", "gpt-4.1")
azure_endpoint = os.environ.get('AZURE_OPENAI_ENDPOINT')
azure_api_key = os.environ.get('AZURE_OPENAI_API_KEY')
azure_deployment = os.environ.get('AZURE_OPENAI_DEPLOYMENT', 'gpt-4.1')
azure_embedding_deployment = os.environ.get(
"AZURE_OPENAI_EMBEDDING_DEPLOYMENT", "text-embedding-3-small"
'AZURE_OPENAI_EMBEDDING_DEPLOYMENT', 'text-embedding-3-small'
)
if not azure_endpoint or not azure_api_key:
raise ValueError("AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY must be set")
raise ValueError('AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY must be set')
async def main():
@ -76,7 +76,7 @@ async def main():
# Initialize Azure OpenAI client
azure_client = AsyncOpenAI(
base_url=f"{azure_endpoint}/openai/v1/",
base_url=f'{azure_endpoint}/openai/v1/',
api_key=azure_api_key,
)
@ -112,40 +112,40 @@ async def main():
# Episodes list containing both text and JSON episodes
episodes = [
{
"content": "Kamala Harris is the Attorney General of California. She was previously "
"the district attorney for San Francisco.",
"type": EpisodeType.text,
"description": "podcast transcript",
'content': 'Kamala Harris is the Attorney General of California. She was previously '
'the district attorney for San Francisco.',
'type': EpisodeType.text,
'description': 'podcast transcript',
},
{
"content": "As AG, Harris was in office from January 3, 2011 January 3, 2017",
"type": EpisodeType.text,
"description": "podcast transcript",
'content': 'As AG, Harris was in office from January 3, 2011 January 3, 2017',
'type': EpisodeType.text,
'description': 'podcast transcript',
},
{
"content": {
"name": "Gavin Newsom",
"position": "Governor",
"state": "California",
"previous_role": "Lieutenant Governor",
"previous_location": "San Francisco",
'content': {
'name': 'Gavin Newsom',
'position': 'Governor',
'state': 'California',
'previous_role': 'Lieutenant Governor',
'previous_location': 'San Francisco',
},
"type": EpisodeType.json,
"description": "podcast metadata",
'type': EpisodeType.json,
'description': 'podcast metadata',
},
]
# Add episodes to the graph
for i, episode in enumerate(episodes):
await graphiti.add_episode(
name=f"California Politics {i}",
name=f'California Politics {i}',
episode_body=(
episode["content"]
if isinstance(episode["content"], str)
else json.dumps(episode["content"])
episode['content']
if isinstance(episode['content'], str)
else json.dumps(episode['content'])
),
source=episode["type"],
source_description=episode["description"],
source=episode['type'],
source_description=episode['description'],
reference_time=datetime.now(timezone.utc),
)
print(f'Added episode: California Politics {i} ({episode["type"].value})')
@ -161,18 +161,18 @@ async def main():
# Perform a hybrid search combining semantic similarity and BM25 retrieval
print("\nSearching for: 'Who was the California Attorney General?'")
results = await graphiti.search("Who was the California Attorney General?")
results = await graphiti.search('Who was the California Attorney General?')
# Print search results
print("\nSearch Results:")
print('\nSearch Results:')
for result in results:
print(f"UUID: {result.uuid}")
print(f"Fact: {result.fact}")
if hasattr(result, "valid_at") and result.valid_at:
print(f"Valid from: {result.valid_at}")
if hasattr(result, "invalid_at") and result.invalid_at:
print(f"Valid until: {result.invalid_at}")
print("---")
print(f'UUID: {result.uuid}')
print(f'Fact: {result.fact}')
if hasattr(result, 'valid_at') and result.valid_at:
print(f'Valid from: {result.valid_at}')
if hasattr(result, 'invalid_at') and result.invalid_at:
print(f'Valid until: {result.invalid_at}')
print('---')
#################################################
# CENTER NODE SEARCH
@ -187,26 +187,26 @@ async def main():
# Get the source node UUID from the top result
center_node_uuid = results[0].source_node_uuid
print("\nReranking search results based on graph distance:")
print(f"Using center node UUID: {center_node_uuid}")
print('\nReranking search results based on graph distance:')
print(f'Using center node UUID: {center_node_uuid}')
reranked_results = await graphiti.search(
"Who was the California Attorney General?",
'Who was the California Attorney General?',
center_node_uuid=center_node_uuid,
)
# Print reranked search results
print("\nReranked Search Results:")
print('\nReranked Search Results:')
for result in reranked_results:
print(f"UUID: {result.uuid}")
print(f"Fact: {result.fact}")
if hasattr(result, "valid_at") and result.valid_at:
print(f"Valid from: {result.valid_at}")
if hasattr(result, "invalid_at") and result.invalid_at:
print(f"Valid until: {result.invalid_at}")
print("---")
print(f'UUID: {result.uuid}')
print(f'Fact: {result.fact}')
if hasattr(result, 'valid_at') and result.valid_at:
print(f'Valid from: {result.valid_at}')
if hasattr(result, 'invalid_at') and result.invalid_at:
print(f'Valid until: {result.invalid_at}')
print('---')
else:
print("No results found in the initial search to use as center node.")
print('No results found in the initial search to use as center node.')
finally:
#################################################
@ -218,8 +218,8 @@ async def main():
# Close the connection
await graphiti.close()
print("\nConnection closed")
print('\nConnection closed')
if __name__ == "__main__":
if __name__ == '__main__':
asyncio.run(main())

View file

@ -540,7 +540,7 @@
"submit_button = widgets.Button(description='Send')\n",
"submit_button.on_click(on_submit)\n",
"\n",
"conversation_output.append_stdout('Asssistant: Hello, how can I help you find shoes today?')\n",
"conversation_output.append_stdout('Assistant: Hello, how can I help you find shoes today?')\n",
"\n",
"display(widgets.VBox([input_box, submit_button, conversation_output]))"
]

View file

@ -37,7 +37,7 @@ else:
logger = logging.getLogger(__name__)
DEFAULT_MODEL = 'gemini-2.5-flash-lite-preview-06-17'
DEFAULT_MODEL = 'gemini-2.5-flash-lite'
class GeminiRerankerClient(CrossEncoderClient):

View file

@ -134,7 +134,7 @@ class KuzuDriver(GraphDriver):
return KuzuDriverSession(self)
async def close(self):
# Do not explicity close the connection, instead rely on GC.
# Do not explicitly close the connection, instead rely on GC.
pass
def delete_all_indexes(self, database_: str):

View file

@ -18,7 +18,9 @@ import logging
from collections.abc import Coroutine
from typing import Any
import neo4j.exceptions
from neo4j import AsyncGraphDatabase, EagerResult
from neo4j.exceptions import ClientError
from typing_extensions import LiteralString
from graphiti_core.driver.driver import GraphDriver, GraphDriverSession, GraphProvider
@ -70,6 +72,15 @@ class Neo4jDriver(GraphDriver):
try:
result = await self.client.execute_query(cypher_query_, parameters_=params, **kwargs)
except neo4j.exceptions.ClientError as e:
# Handle race condition when creating indices/constraints in parallel
# Neo4j 5.26+ may throw EquivalentSchemaRuleAlreadyExists even with IF NOT EXISTS
if 'EquivalentSchemaRuleAlreadyExists' in str(e):
logger.info(f'Index or constraint already exists, continuing: {cypher_query_}')
# Return empty result to indicate success (index exists)
return EagerResult([], None, None) # type: ignore
logger.error(f'Error executing Neo4j query: {e}\n{cypher_query_}\n{params}')
raise
except Exception as e:
logger.error(f'Error executing Neo4j query: {e}\n{cypher_query_}\n{params}')
raise
@ -88,6 +99,21 @@ class Neo4jDriver(GraphDriver):
'CALL db.indexes() YIELD name DROP INDEX name',
)
async def _execute_index_query(self, query: LiteralString) -> EagerResult | None:
"""Execute an index creation query, ignoring 'index already exists' errors.
Neo4j can raise EquivalentSchemaRuleAlreadyExists when concurrent CREATE INDEX
IF NOT EXISTS queries race, even though the index exists. This is safe to ignore.
"""
try:
return await self.execute_query(query)
except ClientError as e:
# Ignore "equivalent index already exists" error (race condition with IF NOT EXISTS)
if 'EquivalentSchemaRuleAlreadyExists' in str(e):
logger.debug(f'Index already exists (concurrent creation): {query[:50]}...')
return None
raise
async def build_indices_and_constraints(self, delete_existing: bool = False):
if delete_existing:
await self.delete_all_indexes()
@ -98,15 +124,8 @@ class Neo4jDriver(GraphDriver):
index_queries: list[LiteralString] = range_indices + fulltext_indices
await semaphore_gather(
*[
self.execute_query(
query,
)
for query in index_queries
]
)
await semaphore_gather(*[self._execute_index_query(query) for query in index_queries])
async def health_check(self) -> None:
"""Check Neo4j connectivity by running the driver's verify_connectivity method."""
try:

View file

@ -33,7 +33,7 @@ class AzureOpenAIEmbedderClient(EmbedderClient):
def __init__(
self,
azure_client: AsyncAzureOpenAI | AsyncOpenAI,
model: str = "text-embedding-3-small",
model: str = 'text-embedding-3-small',
):
self.azure_client = azure_client
self.model = model
@ -44,22 +44,18 @@ class AzureOpenAIEmbedderClient(EmbedderClient):
# Handle different input types
if isinstance(input_data, str):
text_input = [input_data]
elif isinstance(input_data, list) and all(
isinstance(item, str) for item in input_data
):
elif isinstance(input_data, list) and all(isinstance(item, str) for item in input_data):
text_input = input_data
else:
# Convert to string list for other types
text_input = [str(input_data)]
response = await self.azure_client.embeddings.create(
model=self.model, input=text_input
)
response = await self.azure_client.embeddings.create(model=self.model, input=text_input)
# Return the first embedding as a list of floats
return response.data[0].embedding
except Exception as e:
logger.error(f"Error in Azure OpenAI embedding: {e}")
logger.error(f'Error in Azure OpenAI embedding: {e}')
raise
async def create_batch(self, input_data_list: list[str]) -> list[list[float]]:
@ -71,5 +67,5 @@ class AzureOpenAIEmbedderClient(EmbedderClient):
return [embedding.embedding for embedding in response.data]
except Exception as e:
logger.error(f"Error in Azure OpenAI batch embedding: {e}")
logger.error(f'Error in Azure OpenAI batch embedding: {e}')
raise

View file

@ -47,6 +47,9 @@ else:
logger = logging.getLogger(__name__)
AnthropicModel = Literal[
'claude-sonnet-4-5-latest',
'claude-sonnet-4-5-20250929',
'claude-haiku-4-5-latest',
'claude-3-7-sonnet-latest',
'claude-3-7-sonnet-20250219',
'claude-3-5-haiku-latest',
@ -62,7 +65,7 @@ AnthropicModel = Literal[
'claude-2.0',
]
DEFAULT_MODEL: AnthropicModel = 'claude-3-7-sonnet-latest'
DEFAULT_MODEL: AnthropicModel = 'claude-haiku-4-5-latest'
# Maximum output tokens for different Anthropic models
# Based on official Anthropic documentation (as of 2025)
@ -70,6 +73,10 @@ DEFAULT_MODEL: AnthropicModel = 'claude-3-7-sonnet-latest'
# Some models support higher limits with additional configuration (e.g., Claude 3.7 supports
# 128K with 'anthropic-beta: output-128k-2025-02-19' header, but this is not currently implemented).
ANTHROPIC_MODEL_MAX_TOKENS = {
# Claude 4.5 models - 64K tokens
'claude-sonnet-4-5-latest': 65536,
'claude-sonnet-4-5-20250929': 65536,
'claude-haiku-4-5-latest': 65536,
# Claude 3.7 models - standard 64K tokens
'claude-3-7-sonnet-latest': 65536,
'claude-3-7-sonnet-20250219': 65536,

View file

@ -66,21 +66,21 @@ class AzureOpenAILLMClient(BaseOpenAIClient):
"""Create a structured completion using Azure OpenAI's responses.parse API."""
supports_reasoning = self._supports_reasoning_features(model)
request_kwargs = {
"model": model,
"input": messages,
"max_output_tokens": max_tokens,
"text_format": response_model, # type: ignore
'model': model,
'input': messages,
'max_output_tokens': max_tokens,
'text_format': response_model, # type: ignore
}
temperature_value = temperature if not supports_reasoning else None
if temperature_value is not None:
request_kwargs["temperature"] = temperature_value
request_kwargs['temperature'] = temperature_value
if supports_reasoning and reasoning:
request_kwargs["reasoning"] = {"effort": reasoning} # type: ignore
request_kwargs['reasoning'] = {'effort': reasoning} # type: ignore
if supports_reasoning and verbosity:
request_kwargs["text"] = {"verbosity": verbosity} # type: ignore
request_kwargs['text'] = {'verbosity': verbosity} # type: ignore
return await self.client.responses.parse(**request_kwargs)
@ -96,20 +96,20 @@ class AzureOpenAILLMClient(BaseOpenAIClient):
supports_reasoning = self._supports_reasoning_features(model)
request_kwargs = {
"model": model,
"messages": messages,
"max_tokens": max_tokens,
"response_format": {"type": "json_object"},
'model': model,
'messages': messages,
'max_tokens': max_tokens,
'response_format': {'type': 'json_object'},
}
temperature_value = temperature if not supports_reasoning else None
if temperature_value is not None:
request_kwargs["temperature"] = temperature_value
request_kwargs['temperature'] = temperature_value
return await self.client.chat.completions.create(**request_kwargs)
@staticmethod
def _supports_reasoning_features(model: str) -> bool:
"""Return True when the Azure model supports reasoning/verbosity options."""
reasoning_prefixes = ("o1", "o3", "gpt-5")
reasoning_prefixes = ('o1', 'o3', 'gpt-5')
return model.startswith(reasoning_prefixes)

View file

@ -48,7 +48,11 @@ def get_extraction_language_instruction(group_id: str | None = None) -> str:
Returns:
str: Language instruction to append to system messages
"""
return '\n\nAny extracted information should be returned in the same language as it was written in.'
return (
'\n\nAny extracted information should be returned in the same language as it was written in. '
'Only output non-English text when the user has written full sentences or phrases in that non-English language. '
'Otherwise, output English.'
)
logger = logging.getLogger(__name__)

View file

@ -45,7 +45,7 @@ else:
logger = logging.getLogger(__name__)
DEFAULT_MODEL = 'gemini-2.5-flash'
DEFAULT_SMALL_MODEL = 'gemini-2.5-flash-lite-preview-06-17'
DEFAULT_SMALL_MODEL = 'gemini-2.5-flash-lite'
# Maximum output tokens for different Gemini models
GEMINI_MODEL_MAX_TOKENS = {
@ -53,7 +53,6 @@ GEMINI_MODEL_MAX_TOKENS = {
'gemini-2.5-pro': 65536,
'gemini-2.5-flash': 65536,
'gemini-2.5-flash-lite': 64000,
'models/gemini-2.5-flash-lite-preview-06-17': 64000,
# Gemini 2.0 models
'gemini-2.0-flash': 8192,
'gemini-2.0-flash-lite': 8192,

View file

@ -31,8 +31,8 @@ from .errors import RateLimitError, RefusalError
logger = logging.getLogger(__name__)
DEFAULT_MODEL = 'gpt-5-mini'
DEFAULT_SMALL_MODEL = 'gpt-5-nano'
DEFAULT_MODEL = 'gpt-4o-mini'
DEFAULT_SMALL_MODEL = 'gpt-4o-mini'
DEFAULT_REASONING = 'minimal'
DEFAULT_VERBOSITY = 'low'
@ -166,13 +166,17 @@ class BaseOpenAIClient(LLMClient):
except openai.RateLimitError as e:
raise RateLimitError from e
except openai.AuthenticationError as e:
logger.error(f'OpenAI Authentication Error: {e}. Please verify your API key is correct.')
logger.error(
f'OpenAI Authentication Error: {e}. Please verify your API key is correct.'
)
raise
except Exception as e:
# Provide more context for connection errors
error_msg = str(e)
if 'Connection error' in error_msg or 'connection' in error_msg.lower():
logger.error(f'Connection error communicating with OpenAI API. Please check your network connection and API key. Error: {e}')
logger.error(
f'Connection error communicating with OpenAI API. Please check your network connection and API key. Error: {e}'
)
else:
logger.error(f'Error in generating LLM response: {e}')
raise

View file

@ -74,7 +74,9 @@ class OpenAIClient(BaseOpenAIClient):
):
"""Create a structured completion using OpenAI's beta parse API."""
# Reasoning models (gpt-5 family) don't support temperature
is_reasoning_model = model.startswith('gpt-5') or model.startswith('o1') or model.startswith('o3')
is_reasoning_model = (
model.startswith('gpt-5') or model.startswith('o1') or model.startswith('o3')
)
response = await self.client.responses.parse(
model=model,
@ -100,7 +102,9 @@ class OpenAIClient(BaseOpenAIClient):
):
"""Create a regular completion with JSON format."""
# Reasoning models (gpt-5 family) don't support temperature
is_reasoning_model = model.startswith('gpt-5') or model.startswith('o1') or model.startswith('o3')
is_reasoning_model = (
model.startswith('gpt-5') or model.startswith('o1') or model.startswith('o3')
)
return await self.client.chat.completions.create(
model=model,

View file

@ -41,6 +41,16 @@ class DateFilter(BaseModel):
)
class PropertyFilter(BaseModel):
property_name: str = Field(description='Property name')
property_value: str | int | float | None = Field(
description='Value you want to match on for the property'
)
comparison_operator: ComparisonOperator = Field(
description='Comparison operator for the property'
)
class SearchFilters(BaseModel):
node_labels: list[str] | None = Field(
default=None, description='List of node labels to filter on'
@ -53,6 +63,7 @@ class SearchFilters(BaseModel):
created_at: list[list[DateFilter]] | None = Field(default=None)
expired_at: list[list[DateFilter]] | None = Field(default=None)
edge_uuids: list[str] | None = Field(default=None)
property_filters: list[PropertyFilter] | None = Field(default=None)
def cypher_to_opensearch_operator(op: ComparisonOperator) -> str:

View file

@ -17,7 +17,7 @@ limitations under the License.
import re
# Maximum length for entity/node summaries
MAX_SUMMARY_CHARS = 250
MAX_SUMMARY_CHARS = 500
def truncate_at_sentence(text: str, max_chars: int) -> str:

49
mcp_server/.env Normal file
View file

@ -0,0 +1,49 @@
# Graphiti MCP Server Environment Configuration
MCP_SERVER_HOST=gmakai.online
# Neo4j Database Configuration
# These settings are used to connect to your Neo4j database
NEO4J_URI=bolt://neo4j:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=kg3Jsdb2
# OpenAI API Configuration
# Required for LLM operations
OPENAI_API_KEY=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
MODEL_NAME=gpt5.1-nano
# Optional: Only needed for non-standard OpenAI endpoints
OPENAI_BASE_URL=https://openrouter.ai/api/v1
# Optional: Group ID for namespacing graph data
# GROUP_ID=my_project
# Concurrency Control
# Controls how many episodes can be processed simultaneously
# Default: 10 (suitable for OpenAI Tier 3, mid-tier Anthropic)
# Adjust based on your LLM provider's rate limits:
# - OpenAI Tier 1 (free): 1-2
# - OpenAI Tier 2: 5-8
# - OpenAI Tier 3: 10-15
# - OpenAI Tier 4: 20-50
# - Anthropic default: 5-8
# - Anthropic high tier: 15-30
# - Ollama (local): 1-5
# See README.md "Concurrency and LLM Provider 429 Rate Limit Errors" for details
SEMAPHORE_LIMIT=10
# Optional: Path configuration for Docker
# PATH=/root/.local/bin:${PATH}
# Optional: Memory settings for Neo4j (used in Docker Compose)
# NEO4J_server_memory_heap_initial__size=512m
# NEO4J_server_memory_heap_max__size=1G
# NEO4J_server_memory_pagecache_size=512m
# Azure OpenAI configuration
# Optional: Only needed for Azure OpenAI endpoints
# AZURE_OPENAI_ENDPOINT=your_azure_openai_endpoint_here
# AZURE_OPENAI_API_VERSION=2025-01-01-preview
# AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-gpt-4o-mini-deployment
# AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15
# AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-3-large-deployment
# AZURE_OPENAI_USE_MANAGED_IDENTITY=false

View file

@ -8,7 +8,7 @@ server:
llm:
provider: "openai" # Options: openai, azure_openai, anthropic, gemini, groq
model: "gpt-5-mini"
model: "gpt-4o-mini"
max_tokens: 4096
providers:

View file

@ -8,7 +8,7 @@ server:
llm:
provider: "openai" # Options: openai, azure_openai, anthropic, gemini, groq
model: "gpt-5-mini"
model: "gpt-4o-mini"
max_tokens: 4096
providers:

View file

@ -8,7 +8,7 @@ server:
llm:
provider: "openai" # Options: openai, azure_openai, anthropic, gemini, groq
model: "gpt-5-mini"
model: "gpt-4o-mini"
max_tokens: 4096
providers:

View file

@ -12,7 +12,7 @@ server:
llm:
provider: "openai" # Options: openai, azure_openai, anthropic, gemini, groq
model: "gpt-5-mini"
model: "gpt-4o-mini"
max_tokens: 4096
providers:

View file

@ -1,23 +1,23 @@
services:
neo4j:
image: neo4j:5.26.0
image: neo4j:latest
ports:
- "7474:7474" # HTTP
- "7687:7687" # Bolt
environment:
- NEO4J_AUTH=${NEO4J_USER:-neo4j}/${NEO4J_PASSWORD:-demodemo}
- NEO4J_server_memory_heap_initial__size=512m
- NEO4J_server_memory_heap_max__size=1G
- NEO4J_server_memory_pagecache_size=512m
- NEO4J_AUTH=${NEO4J_USER:-neo4j}/${NEO4J_PASSWORD:-kg3Jsdb2}
volumes:
- neo4j_data:/data
- neo4j_logs:/logs
- /data/neo4j/data:/data
- /data/neo4j/logs:/logs
- /data/neo4j/plugins:/plugins
- /data/neo4j/config:/config
healthcheck:
test: ["CMD", "wget", "-O", "/dev/null", "http://localhost:7474"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: always
graphiti-mcp:
image: zepai/knowledge-graph-mcp:standalone
@ -27,9 +27,9 @@ services:
build:
context: ..
dockerfile: docker/Dockerfile.standalone
env_file:
- path: ../.env
required: false
#env_file:
# - path: ../.env
# required: true
depends_on:
neo4j:
condition: service_healthy
@ -37,13 +37,18 @@ services:
# Database configuration
- NEO4J_URI=${NEO4J_URI:-bolt://neo4j:7687}
- NEO4J_USER=${NEO4J_USER:-neo4j}
- NEO4J_PASSWORD=${NEO4J_PASSWORD:-demodemo}
- NEO4J_PASSWORD=${NEO4J_PASSWORD:-kg3Jsdb2}
- NEO4J_DATABASE=${NEO4J_DATABASE:-neo4j}
# Application configuration
- GRAPHITI_GROUP_ID=${GRAPHITI_GROUP_ID:-main}
- SEMAPHORE_LIMIT=${SEMAPHORE_LIMIT:-10}
- CONFIG_PATH=/app/mcp/config/config.yaml
- PATH=/root/.local/bin:${PATH}
- MCP_SERVER_HOST=gmakai.online
- OPENAI_API_KEY=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
- MODEL_NAME=gpt5.1-nano
- OPENAI_BASE_URL=https://openrouter.ai/api/v1
volumes:
- ../config/config-docker-neo4j.yaml:/app/mcp/config/config.yaml:ro
ports:

View file

@ -147,7 +147,7 @@ class LLMConfig(BaseModel):
"""LLM configuration."""
provider: str = Field(default='openai', description='LLM provider')
model: str = Field(default='gpt-4.1', description='Model name')
model: str = Field(default='gpt-4o-mini', description='Model name')
temperature: float | None = Field(
default=None, description='Temperature (optional, defaults to None for reasoning models)'
)

View file

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

View file

@ -455,6 +455,62 @@
"created_at": "2025-11-06T08:39:46Z",
"repoId": 840056306,
"pullRequestNo": 1053
},
{
"name": "supmo668",
"id": 28805779,
"comment_id": 3550309664,
"created_at": "2025-11-19T01:56:25Z",
"repoId": 840056306,
"pullRequestNo": 1072
},
{
"name": "donbr",
"id": 7340008,
"comment_id": 3568970102,
"created_at": "2025-11-24T05:19:42Z",
"repoId": 840056306,
"pullRequestNo": 1081
},
{
"name": "apetti1920",
"id": 4706645,
"comment_id": 3572726648,
"created_at": "2025-11-24T21:07:34Z",
"repoId": 840056306,
"pullRequestNo": 1084
},
{
"name": "ZLBillShaw",
"id": 55940186,
"comment_id": 3583997833,
"created_at": "2025-11-27T02:45:53Z",
"repoId": 840056306,
"pullRequestNo": 1085
},
{
"name": "ronaldmego",
"id": 17481958,
"comment_id": 3617267429,
"created_at": "2025-12-05T14:59:42Z",
"repoId": 840056306,
"pullRequestNo": 1094
},
{
"name": "NShumway",
"id": 29358113,
"comment_id": 3634967978,
"created_at": "2025-12-10T01:26:49Z",
"repoId": 840056306,
"pullRequestNo": 1102
},
{
"name": "husniadil",
"id": 10581130,
"comment_id": 3650156180,
"created_at": "2025-12-14T03:37:59Z",
"repoId": 840056306,
"pullRequestNo": 1105
}
]
}

View file

@ -81,7 +81,7 @@ class TestAnthropicClientInitialization:
config = LLMConfig(api_key='test_api_key')
client = AnthropicClient(config=config, cache=False)
assert client.model == 'claude-3-7-sonnet-latest'
assert client.model == 'claude-haiku-4-5-latest'
@patch.dict(os.environ, {'ANTHROPIC_API_KEY': 'env_api_key'})
def test_init_without_config(self):
@ -89,7 +89,7 @@ class TestAnthropicClientInitialization:
client = AnthropicClient(cache=False)
assert client.config.api_key == 'env_api_key'
assert client.model == 'claude-3-7-sonnet-latest'
assert client.model == 'claude-haiku-4-5-latest'
def test_init_with_custom_client(self):
"""Test initialization with a custom AsyncAnthropic client."""

View file

@ -455,7 +455,6 @@ class TestGeminiClientGenerateResponse:
('gemini-2.5-flash', 65536),
('gemini-2.5-pro', 65536),
('gemini-2.5-flash-lite', 64000),
('models/gemini-2.5-flash-lite-preview-06-17', 64000),
('gemini-2.0-flash', 8192),
('gemini-1.5-pro', 8192),
('gemini-1.5-flash', 8192),

View file

@ -87,7 +87,7 @@ def test_truncate_at_sentence_strips_trailing_whitespace():
def test_max_summary_chars_constant():
"""Test that MAX_SUMMARY_CHARS is set to expected value."""
assert MAX_SUMMARY_CHARS == 250
assert MAX_SUMMARY_CHARS == 500
def test_truncate_at_sentence_realistic_summary():

137
uv.lock generated
View file

@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 2
requires-python = ">=3.10, <4"
resolution-markers = [
"python_full_version >= '3.14'",
@ -262,35 +262,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
]
[[package]]
name = "azure-core"
version = "1.36.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "requests" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0a/c4/d4ff3bc3ddf155156460bff340bbe9533f99fac54ddea165f35a8619f162/azure_core-1.36.0.tar.gz", hash = "sha256:22e5605e6d0bf1d229726af56d9e92bc37b6e726b141a18be0b4d424131741b7", size = 351139, upload-time = "2025-10-15T00:33:49.083Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/3c/b90d5afc2e47c4a45f4bba00f9c3193b0417fad5ad3bb07869f9d12832aa/azure_core-1.36.0-py3-none-any.whl", hash = "sha256:fee9923a3a753e94a259563429f3644aaf05c486d45b1215d098115102d91d3b", size = 213302, upload-time = "2025-10-15T00:33:51.058Z" },
]
[[package]]
name = "azure-identity"
version = "1.25.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "azure-core" },
{ name = "cryptography" },
{ name = "msal" },
{ name = "msal-extensions" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/8d/1a6c41c28a37eab26dc85ab6c86992c700cd3f4a597d9ed174b0e9c69489/azure_identity-1.25.1.tar.gz", hash = "sha256:87ca8328883de6036443e1c37b40e8dc8fb74898240f61071e09d2e369361456", size = 279826, upload-time = "2025-10-06T20:30:02.194Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/83/7b/5652771e24fff12da9dde4c20ecf4682e606b104f26419d139758cc935a6/azure_identity-1.25.1-py3-none-any.whl", hash = "sha256:e9edd720af03dff020223cd269fa3a61e8f345ea75443858273bcb44844ab651", size = 191317, upload-time = "2025-10-06T20:30:04.251Z" },
]
[[package]]
name = "babel"
version = "2.17.0"
@ -549,71 +520,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180, upload-time = "2024-03-12T16:53:39.226Z" },
]
[[package]]
name = "cryptography"
version = "46.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
{ url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
{ url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
{ url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
{ url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
{ url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
{ url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
{ url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
{ url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
{ url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
{ url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
{ url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
{ url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" },
{ url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" },
{ url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" },
{ url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
{ url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
{ url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
{ url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
{ url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
{ url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
{ url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
{ url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
{ url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
{ url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
{ url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
{ url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
{ url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" },
{ url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" },
{ url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" },
{ url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
{ url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
{ url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
{ url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
{ url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
{ url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
{ url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
{ url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
{ url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
{ url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
{ url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
{ url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
{ url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" },
{ url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" },
{ url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
{ url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" },
{ url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474, upload-time = "2025-10-15T23:18:15.477Z" },
{ url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" },
{ url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" },
{ url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" },
{ url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" },
{ url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" },
{ url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" },
]
[[package]]
name = "debugpy"
version = "1.8.14"
@ -902,7 +808,7 @@ wheels = [
[[package]]
name = "graphiti-core"
version = "0.23.1"
version = "0.24.3"
source = { editable = "." }
dependencies = [
{ name = "diskcache" },
@ -920,12 +826,8 @@ dependencies = [
anthropic = [
{ name = "anthropic" },
]
azure = [
{ name = "azure-identity" },
]
dev = [
{ name = "anthropic" },
{ name = "azure-identity" },
{ name = "boto3" },
{ name = "diskcache-stubs" },
{ name = "falkordb" },
@ -986,8 +888,6 @@ voyageai = [
requires-dist = [
{ name = "anthropic", marker = "extra == 'anthropic'", specifier = ">=0.49.0" },
{ name = "anthropic", marker = "extra == 'dev'", specifier = ">=0.49.0" },
{ name = "azure-identity", marker = "extra == 'azure'", specifier = ">=1.25.1" },
{ name = "azure-identity", marker = "extra == 'dev'", specifier = ">=1.25.1" },
{ name = "boto3", marker = "extra == 'dev'", specifier = ">=1.39.16" },
{ name = "boto3", marker = "extra == 'neo4j-opensearch'", specifier = ">=1.39.16" },
{ name = "boto3", marker = "extra == 'neptune'", specifier = ">=1.39.16" },
@ -1033,7 +933,7 @@ requires-dist = [
{ name = "voyageai", marker = "extra == 'dev'", specifier = ">=0.2.3" },
{ name = "voyageai", marker = "extra == 'voyageai'", specifier = ">=0.2.3" },
]
provides-extras = ["anthropic", "groq", "google-genai", "kuzu", "falkordb", "voyageai", "neo4j-opensearch", "sentence-transformers", "neptune", "tracing", "azure", "dev"]
provides-extras = ["anthropic", "groq", "google-genai", "kuzu", "falkordb", "voyageai", "neo4j-opensearch", "sentence-transformers", "neptune", "tracing", "dev"]
[[package]]
name = "groq"
@ -1811,32 +1711,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
]
[[package]]
name = "msal"
version = "1.34.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "pyjwt", extra = ["crypto"] },
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cf/0e/c857c46d653e104019a84f22d4494f2119b4fe9f896c92b4b864b3b045cc/msal-1.34.0.tar.gz", hash = "sha256:76ba83b716ea5a6d75b0279c0ac353a0e05b820ca1f6682c0eb7f45190c43c2f", size = 153961, upload-time = "2025-09-22T23:05:48.989Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/dc/18d48843499e278538890dc709e9ee3dea8375f8be8e82682851df1b48b5/msal-1.34.0-py3-none-any.whl", hash = "sha256:f669b1644e4950115da7a176441b0e13ec2975c29528d8b9e81316023676d6e1", size = 116987, upload-time = "2025-09-22T23:05:47.294Z" },
]
[[package]]
name = "msal-extensions"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "msal" },
]
sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" },
]
[[package]]
name = "multidict"
version = "6.6.3"
@ -2940,11 +2814,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344, upload-time = "2024-08-01T15:01:06.481Z" },
]
[package.optional-dependencies]
crypto = [
{ name = "cryptography" },
]
[[package]]
name = "pyright"
version = "1.1.404"