From 3ce3962e4baa9db04d1be0cff68d67ec425abf25 Mon Sep 17 00:00:00 2001 From: Daniel Chalef <131175+danielchalef@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:07:46 -0800 Subject: [PATCH 1/9] @apetti1920 has signed the CLA in getzep/graphiti#1084 --- signatures/version1/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json index 99153730..dbc52678 100644 --- a/signatures/version1/cla.json +++ b/signatures/version1/cla.json @@ -471,6 +471,14 @@ "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 } ] } \ No newline at end of file From 422558d06cc6c148602c9748b1aa8c0d438528f1 Mon Sep 17 00:00:00 2001 From: Daniel Chalef <131175+danielchalef@users.noreply.github.com> Date: Wed, 26 Nov 2025 18:46:03 -0800 Subject: [PATCH 2/9] @ZLBillShaw has signed the CLA in getzep/graphiti#1085 --- signatures/version1/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json index dbc52678..22dcdaa7 100644 --- a/signatures/version1/cla.json +++ b/signatures/version1/cla.json @@ -479,6 +479,14 @@ "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 } ] } \ No newline at end of file From bddd46916724cd670ff5e64574731879576e62eb Mon Sep 17 00:00:00 2001 From: Daniel Chalef <131175+danielchalef@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:50:07 -0800 Subject: [PATCH 3/9] Disable issue triage and daily maintenance workflows (#1089) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/daily_issue_maintenance.yml | 123 --------------- .github/workflows/issue-triage.yml | 141 ------------------ 2 files changed, 264 deletions(-) delete mode 100644 .github/workflows/daily_issue_maintenance.yml delete mode 100644 .github/workflows/issue-triage.yml diff --git a/.github/workflows/daily_issue_maintenance.yml b/.github/workflows/daily_issue_maintenance.yml deleted file mode 100644 index 062b7b4f..00000000 --- a/.github/workflows/daily_issue_maintenance.yml +++ /dev/null @@ -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 diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml deleted file mode 100644 index 2080e5ae..00000000 --- a/.github/workflows/issue-triage.yml +++ /dev/null @@ -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 From 2d262cf22d3e14405cf195132c53194798dabad8 Mon Sep 17 00:00:00 2001 From: Preston Rasmussen <109292228+prasmussen15@users.noreply.github.com> Date: Wed, 3 Dec 2025 08:47:12 -0800 Subject: [PATCH 4/9] foreign language fix (#1090) --- graphiti_core/llm_client/client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/graphiti_core/llm_client/client.py b/graphiti_core/llm_client/client.py index 0f51dc04..324a82ee 100644 --- a/graphiti_core/llm_client/client.py +++ b/graphiti_core/llm_client/client.py @@ -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__) From 6dc7b8800b61c84815275d491ca721f5c22f741b Mon Sep 17 00:00:00 2001 From: Preston Rasmussen <109292228+prasmussen15@users.noreply.github.com> Date: Thu, 4 Dec 2025 10:11:33 -0800 Subject: [PATCH 5/9] bump (#1093) update --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7927dc72..0a569527 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "graphiti-core" description = "A temporal graph building library" -version = "0.24.1" +version = "0.24.2" authors = [ { name = "Paul Paliychuk", email = "paul@getzep.com" }, { name = "Preston Rasmussen", email = "preston@getzep.com" }, From 2d50fa565d5952bd2801ee391f2f319595a23dca Mon Sep 17 00:00:00 2001 From: Daniel Chalef <131175+danielchalef@users.noreply.github.com> Date: Fri, 5 Dec 2025 06:59:53 -0800 Subject: [PATCH 6/9] @ronaldmego has signed the CLA in getzep/graphiti#1094 --- signatures/version1/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json index 22dcdaa7..4bf7b731 100644 --- a/signatures/version1/cla.json +++ b/signatures/version1/cla.json @@ -487,6 +487,14 @@ "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 } ] } \ No newline at end of file From 24105b955669f6473ad0324618f452cda07896f9 Mon Sep 17 00:00:00 2001 From: Preston Rasmussen <109292228+prasmussen15@users.noreply.github.com> Date: Mon, 8 Dec 2025 07:50:19 -0800 Subject: [PATCH 7/9] add property filters (#1099) * add property filters * update --- graphiti_core/search/search_filters.py | 11 +++++++++++ pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/graphiti_core/search/search_filters.py b/graphiti_core/search/search_filters.py index 1534b926..063e846e 100644 --- a/graphiti_core/search/search_filters.py +++ b/graphiti_core/search/search_filters.py @@ -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: diff --git a/pyproject.toml b/pyproject.toml index 0a569527..d5ae9fb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "graphiti-core" description = "A temporal graph building library" -version = "0.24.2" +version = "0.24.3" authors = [ { name = "Paul Paliychuk", email = "paul@getzep.com" }, { name = "Preston Rasmussen", email = "preston@getzep.com" }, diff --git a/uv.lock b/uv.lock index 546537e1..25ca2f08 100644 --- a/uv.lock +++ b/uv.lock @@ -808,7 +808,7 @@ wheels = [ [[package]] name = "graphiti-core" -version = "0.24.1" +version = "0.24.3" source = { editable = "." } dependencies = [ { name = "diskcache" }, From 1de752646a9557682c762b83a679d46ffc67e821 Mon Sep 17 00:00:00 2001 From: Ronald Mego Solano <17481958+ronaldmego@users.noreply.github.com> Date: Mon, 8 Dec 2025 11:31:04 -0500 Subject: [PATCH 8/9] 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 --- graphiti_core/driver/neo4j_driver.py | 25 +++++++++++++------ .../llm_client/openai_base_client.py | 4 +-- .../config-docker-falkordb-combined.yaml | 2 +- mcp_server/config/config-docker-falkordb.yaml | 2 +- mcp_server/config/config-docker-neo4j.yaml | 2 +- mcp_server/config/config.yaml | 2 +- mcp_server/src/config/schema.py | 2 +- 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/graphiti_core/driver/neo4j_driver.py b/graphiti_core/driver/neo4j_driver.py index 4fa73f57..f5a815fd 100644 --- a/graphiti_core/driver/neo4j_driver.py +++ b/graphiti_core/driver/neo4j_driver.py @@ -19,6 +19,7 @@ from collections.abc import Coroutine from typing import Any from neo4j import AsyncGraphDatabase, EagerResult +from neo4j.exceptions import ClientError from typing_extensions import LiteralString from graphiti_core.driver.driver import GraphDriver, GraphDriverSession, GraphProvider @@ -88,6 +89,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,14 +114,7 @@ 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.""" diff --git a/graphiti_core/llm_client/openai_base_client.py b/graphiti_core/llm_client/openai_base_client.py index 9580ff03..facb1f8b 100644 --- a/graphiti_core/llm_client/openai_base_client.py +++ b/graphiti_core/llm_client/openai_base_client.py @@ -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' diff --git a/mcp_server/config/config-docker-falkordb-combined.yaml b/mcp_server/config/config-docker-falkordb-combined.yaml index 150e06d8..cb0e73b6 100644 --- a/mcp_server/config/config-docker-falkordb-combined.yaml +++ b/mcp_server/config/config-docker-falkordb-combined.yaml @@ -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: diff --git a/mcp_server/config/config-docker-falkordb.yaml b/mcp_server/config/config-docker-falkordb.yaml index 5bc8f14d..a1c8c918 100644 --- a/mcp_server/config/config-docker-falkordb.yaml +++ b/mcp_server/config/config-docker-falkordb.yaml @@ -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: diff --git a/mcp_server/config/config-docker-neo4j.yaml b/mcp_server/config/config-docker-neo4j.yaml index f2ab6e58..d10ec219 100644 --- a/mcp_server/config/config-docker-neo4j.yaml +++ b/mcp_server/config/config-docker-neo4j.yaml @@ -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: diff --git a/mcp_server/config/config.yaml b/mcp_server/config/config.yaml index 91f72377..f0c69de5 100644 --- a/mcp_server/config/config.yaml +++ b/mcp_server/config/config.yaml @@ -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: diff --git a/mcp_server/src/config/schema.py b/mcp_server/src/config/schema.py index 990d96c4..ff7609bd 100644 --- a/mcp_server/src/config/schema.py +++ b/mcp_server/src/config/schema.py @@ -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)' ) From 5e593dd09638eed8e37742d046911ec5a8cd5a28 Mon Sep 17 00:00:00 2001 From: Daniel Chalef <131175+danielchalef@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:26:59 -0800 Subject: [PATCH 9/9] @NShumway has signed the CLA in getzep/graphiti#1102 --- signatures/version1/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json index 4bf7b731..e7bd2050 100644 --- a/signatures/version1/cla.json +++ b/signatures/version1/cla.json @@ -495,6 +495,14 @@ "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 } ] } \ No newline at end of file