Skip to content

Fix memory saving#196

Closed
galshubeli wants to merge 3 commits intostagingfrom
fix-memory-saving
Closed

Fix memory saving#196
galshubeli wants to merge 3 commits intostagingfrom
fix-memory-saving

Conversation

@galshubeli
Copy link
Copy Markdown
Collaborator

@galshubeli galshubeli commented Sep 3, 2025

Summary by CodeRabbit

  • New Features

    • Message input and refresh controls are disabled by default until a database is selected.
    • Selecting a database automatically enables the refresh and Send controls.
  • Bug Fixes

    • Prevents duplicate saved queries in memory.
    • Removed disruptive alerts when no database is selected; actions now safely no-op.
  • Refactor

    • Streamlined graph selection and message send flow for a smoother experience.
  • Chores

    • Updated embedding configuration for improved provider compatibility.

@galshubeli galshubeli requested a review from Copilot September 3, 2025 10:15
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Backend now creates/fetches Graphiti entities directly via Cypher, adds an embedding-model name extractor, standardizes embedding_dim=1536, and expands MemoryTool.create. Frontend disables input/refresh by default, re-enables them on graph selection, removes a user alert, and updates addMessage to accept an isLoading flag.

Changes

Cohort / File(s) Summary
Graphiti memory backend
api/memory/graphiti_tool.py
Added extract_embedding_model_name(full_model_name); expanded MemoryTool.create(user_id, graph_id, use_direct_entities=True); added _ensure_entity_nodes_direct to MERGE User/Database nodes and HAS_DATABASE via Cypher (with uuid, created_at, name_embedding); refactored save_query_memory to use direct Cypher lookups and avoid duplicates; set embedding_dim=1536 and derive embedding model from Config.EMBEDDING_MODEL_NAME; added import uuid.
Templates: disabled by default
app/templates/components/chat_header.j2, app/templates/components/chat_input.j2
Added disabled to graph refresh button and message input so they start non-interactive.
App bootstrap logic
app/ts/app.ts
Removed alert in graphRefresh guard; now early-returns when no valid graph selected.
Chat module and message API
app/ts/modules/chat.ts, app/ts/modules/messages.ts
sendMessage no longer emits a user alert when no graph is selected and returns silently; addMessage call updated to addMessage(message, "user", false, currentUser) and addMessage signature extended to accept isLoading.
Graph selector interactions
app/ts/modules/graph_select.ts
On graph option click, enables DOM.graphSelectRefresh and DOM.submitButton (if present) before selecting graph, invoking onSelect, and closing options.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant MemoryTool
  participant GraphDB as Graph DB (Cypher)
  participant Embedder

  rect rgba(200,230,255,0.12)
  note over Client,MemoryTool: Updated creation flow
  Client->>MemoryTool: create(user_id, graph_id, use_direct_entities=True)
  MemoryTool->>GraphDB: MERGE User, Database, HAS_DATABASE (uuid, created_at, summary, name_embedding)
  MemoryTool->>Embedder: embed(name/summary)
  Embedder-->>MemoryTool: embedding
  MemoryTool-->>Client: MemoryTool instance
  end

  rect rgba(200,255,200,0.12)
  note over Client,MemoryTool: Save query flow
  Client->>MemoryTool: save_query_memory(query, summary, ...)
  MemoryTool->>GraphDB: MATCH Database by graph_id → get database_node_uuid
  MemoryTool->>GraphDB: CHECK existing Query node (by database_node_uuid + query)
  alt not exists
    MemoryTool->>Embedder: embed(query/summary)
    Embedder-->>MemoryTool: embedding
    MemoryTool->>GraphDB: CREATE Query node + RELATION
  else exists
    Note right of GraphDB: Skip creation
  end
  MemoryTool-->>Client: created | skipped
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant UI as UI (Buttons/Input)
  participant GraphSelect
  participant Chat

  rect rgba(255,240,200,0.12)
  note over UI: Initial disabled state
  UI->>UI: Refresh button disabled
  UI->>UI: Message input disabled
  end

  rect rgba(220,255,220,0.12)
  User->>GraphSelect: choose graph
  GraphSelect->>UI: enable refresh button & submit button (if present)
  GraphSelect->>GraphSelect: setSelectedGraph / onSelect / close options
  end

  rect rgba(255,220,220,0.12)
  User->>Chat: sendMessage()
  Chat->>Chat: guard: if no graph → return silently
  Chat->>UI: addMessage(message, "user", false, currentUser)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Add Memory component #150 — Modifies api/memory/graphiti_tool.py around MemoryTool creation, save logic, and embedding/config wiring; closely related at code level.

Suggested reviewers

  • gkorland

Poem

I thump my paws, nodes stitched by light,
Cypher seeds planted through the night.
Buttons wake when graphs are picked,
Silent guards keep chatter strict.
Embeddings snug at one-five-three-six—hare hops, commit! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 93766bc and 9c51a1c.

📒 Files selected for processing (1)
  • app/templates/components/chat_input.j2 (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/templates/components/chat_input.j2
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: e2e-tests
  • GitHub Check: unit-tests
  • GitHub Check: e2e-tests
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-memory-saving

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Sep 3, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes memory saving functionality by improving the user interface behavior when selecting databases and refactoring the Graphiti memory tool implementation. The changes focus on enabling UI controls when a database is selected and streamlining the backend memory management code.

Key changes:

  • Enhanced UI state management by enabling input controls when a database is selected
  • Simplified frontend validation logic by removing error messages and alerts
  • Refactored Graphiti memory tool to use direct Cypher queries instead of search operations

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
app/ts/modules/graph_select.ts Enables submit button and refresh button when a graph is selected
app/ts/modules/chat.ts Removes error message display and adds parameter to addMessage call
app/ts/app.ts Removes alert dialog for database selection validation
app/templates/components/chat_input.j2 Sets message input field to disabled by default
app/templates/components/chat_header.j2 Sets refresh button to disabled by default
api/memory/graphiti_tool.py Major refactor replacing search-based operations with direct Cypher queries and improved embedding model handling

Comment thread app/ts/modules/chat.ts
if (state.currentRequestController) {
state.currentRequestController.abort();
}

Copy link

Copilot AI Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of a false parameter to addMessage changes the function signature without clear documentation of what this boolean parameter represents. Consider using a named parameter or adding a comment to clarify the purpose of this parameter.

Copilot uses AI. Check for mistakes.
Comment thread api/memory/graphiti_tool.py
Comment thread api/memory/graphiti_tool.py
Comment thread api/memory/graphiti_tool.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
api/memory/graphiti_tool.py (3)

62-75: Honor use_direct_entities and make index creation idempotent

  • The use_direct_entities argument is unused; callers can’t opt out.
  • CREATE VECTOR INDEX will fail on repeated calls; make it idempotent.
 @classmethod
-async def create(cls, user_id: str, graph_id: str, use_direct_entities: bool = True) -> "MemoryTool":
+async def create(cls, user_id: str, graph_id: str, use_direct_entities: bool = True) -> "MemoryTool":
     """Async factory to construct and initialize the tool."""
     self = cls(user_id, graph_id)
-
-    await self._ensure_entity_nodes_direct(user_id, graph_id)
+    if use_direct_entities:
+        await self._ensure_entity_nodes_direct(user_id, graph_id)
+    else:
+        # Fallback to legacy episode-based creation
+        await self._ensure_user_node(user_id)
+        await self._ensure_database_node(graph_id, user_id)
 
-    vector_size = Config.EMBEDDING_MODEL.get_vector_size()
-    driver = self.graphiti_client.driver
-    await driver.execute_query(f"CREATE VECTOR INDEX FOR (p:Query) ON (p.embeddings) OPTIONS {{dimension:{vector_size}, similarityFunction:'euclidean'}}")
+    vector_size = Config.EMBEDDING_MODEL.get_vector_size()
+    driver = self.graphiti_client.driver
+    try:
+        await driver.execute_query(
+            f"CREATE VECTOR INDEX FOR (p:Query) ON (p.embeddings) "
+            f"OPTIONS {{dimension:{vector_size}, similarityFunction:'euclidean'}}"
+        )
+    except Exception as e:  # keep soft-idempotent; avoid startup failure
+        msg = str(e).lower()
+        if "already exists" not in msg:
+            print(f"Error creating vector index on Query.embeddings: {e}")
 
     return self

Also applies to: 70-73


784-809: Avoid hardcoding embedding_dim; derive from Config to prevent mismatches

Hardcoding 1536 risks index/embedding dimension mismatch if the model changes (e.g., text-embedding-3-large = 3072).

-            embedder=OpenAIEmbedder(
-                config=OpenAIEmbedderConfig(
-                    embedding_model=config.embedding_deployment,
-                    embedding_dim=1536
-                ),
+            embedder=OpenAIEmbedder(
+                config=OpenAIEmbedderConfig(
+                    embedding_model=config.embedding_deployment,
+                    embedding_dim=Config.EMBEDDING_MODEL.get_vector_size(),
+                ),
                 client=embedding_client_azure,
             ),
...
-        graphiti_client = Graphiti(
+        graphiti_client = Graphiti(
             graph_driver=falkor_driver,
             embedder=OpenAIEmbedder(
                 config=OpenAIEmbedderConfig(
-                    embedding_model=embedding_model_name,
-                    embedding_dim=1536
+                    embedding_model=embedding_model_name,
+                    embedding_dim=Config.EMBEDDING_MODEL.get_vector_size(),
                 )
             ),
         )

336-367: Use Cypher parameters for strings; avoid manual escaping and injection pitfalls

String interpolation with escaped quotes is brittle and error-prone. Parameterize user_query, sql_query, and error; keep relationship type derived from a boolean.

-            # First check if a Query node with the same user_query and sql_query already exists
-            check_query = f"""
-            MATCH (db:Entity {{uuid: "{database_node_uuid}"}})
-            MATCH (db)-[r]->(q:Query)
-            WHERE q.user_query = "{escaped_query}" AND q.sql_query = "{escaped_sql}"
-            RETURN q.uuid as existing_query_uuid
-            LIMIT 1
-            """
-            
-            graph_driver = self.graphiti_client.driver
-            check_result = await graph_driver.execute_query(check_query)
+            # First check if a Query node with the same user_query and sql_query already exists
+            check_query = """
+            MATCH (db:Entity {uuid: $db_uuid})-[]->(q:Query)
+            WHERE q.user_query = $user_query AND q.sql_query = $sql_query
+            RETURN q.uuid as existing_query_uuid
+            LIMIT 1
+            """
+            graph_driver = self.graphiti_client.driver
+            check_result = await graph_driver.execute_query(
+                check_query,
+                db_uuid=database_node_uuid,
+                user_query=query,
+                sql_query=sql_query,
+            )
...
-            cypher_query = f"""
-            MATCH (db:Entity {{uuid: "{database_node_uuid}"}})
-            MERGE (q:Query {{
-                user_query: "{escaped_query}",
-                sql_query: "{escaped_sql}",
-                success: {str(success).lower()},
-                error: "{escaped_error}",
-                timestamp: timestamp(),
-                embeddings: vecf32($embedding)
-            }})
-            CREATE (db)-[:{relationship_type} {{timestamp: timestamp()}}]->(q)
-            RETURN q.uuid as query_uuid
-            """
+            cypher_query = f"""
+            MATCH (db:Entity {{uuid: $db_uuid}})
+            MERGE (q:Query {{
+                user_query: $user_query,
+                sql_query: $sql_query,
+                success: $success,
+                error: $error,
+                timestamp: timestamp(),
+                embeddings: vecf32($embedding)
+            }})
+            CREATE (db)-[:{relationship_type} {{timestamp: timestamp()}}]->(q)
+            RETURN q.uuid as query_uuid
+            """
...
-                result = await graph_driver.execute_query(cypher_query, embedding=embeddings)
+                result = await graph_driver.execute_query(
+                    cypher_query,
+                    db_uuid=database_node_uuid,
+                    user_query=query,
+                    sql_query=sql_query,
+                    success=success,
+                    error=error or "",
+                    embedding=embeddings,
+                )

Also applies to: 318-326

app/ts/app.ts (1)

89-106: Loading spinner can get stuck on refresh error.

On a non-2xx response you return before removing the loading class and clearing title, leaving the button in a perpetual loading state. Use a try/finally around the refresh flow and move the cleanup into finally.

   DOM.graphSelectRefresh?.addEventListener("click", async () => {
     const selected = getSelectedGraph();
     const refreshButton = DOM.graphSelectRefresh;

     if (!refreshButton) return;

-    if (!selected || selected === "Select Database") return
+    if (!selected || selected === "Select Database") return;

     refreshButton.classList.add("loading");
+    refreshButton.title = "";
-
-    const result = await fetch(
-      `/graphs/${encodeURIComponent(selected)}/refresh`,
-      {
-        method: "POST",
-      }
-    );
-
-    if (!result.ok) {
-      console.error(
-        "Failed to refresh graph:",
-        result.status,
-        result.statusText
-      );
-      return;
-    }
-
-    if (result.body) {
-      const reader = result.body.getReader();
-      const decoder = new TextDecoder();
-      let buffer = "";
-
-      try {
-        while (true) {
-          const { done, value } = await reader.read();
-          if (done) break;
-
-          buffer += decoder.decode(value, { stream: true });
-
-          // Process complete messages separated by boundary
-          const messages = buffer.split("|||FALKORDB_MESSAGE_BOUNDARY|||");
-          buffer = messages.pop() || ""; // Keep incomplete message in buffer
-
-          for (const message of messages) {
-            if (message.trim()) {
-              try {
-                const parsed = JSON.parse(message.trim());
-
-                if (parsed.type === "reasoning_step") {
-                  refreshButton.title += ` ${parsed.message}\n`;
-                } else if (parsed.type === "final_result") {
-                  refreshButton.title += ` ${parsed.message}`;
-                }
-              } catch (e) {
-                console.warn("Failed to parse message:", message, e);
-              }
-            }
-          }
-        }
-      } finally {
-        reader.releaseLock();
-      }
-    }
-
-    if (DOM.schemaContainer && DOM.schemaContainer.classList.contains("open")) {
-      await loadAndShowGraph(selected);
-      setTimeout(resizeGraph, 450);
-    }
-
-    refreshButton.classList.remove("loading");
-    refreshButton.title = "";
+    try {
+      const result = await fetch(
+        `/graphs/${encodeURIComponent(selected)}/refresh`,
+        { method: "POST" }
+      );
+
+      if (!result.ok) {
+        console.error(
+          "Failed to refresh graph:",
+          result.status,
+          result.statusText
+        );
+        return; // finally will still run
+      }
+
+      if (result.body) {
+        const reader = result.body.getReader();
+        const decoder = new TextDecoder();
+        let buffer = "";
+        try {
+          while (true) {
+            const { done, value } = await reader.read();
+            if (done) break;
+            buffer += decoder.decode(value, { stream: true });
+            const messages = buffer.split("|||FALKORDB_MESSAGE_BOUNDARY|||");
+            buffer = messages.pop() || "";
+            for (const message of messages) {
+              if (!message.trim()) continue;
+              try {
+                const parsed = JSON.parse(message.trim());
+                if (parsed.type === "reasoning_step") {
+                  refreshButton.title += ` ${parsed.message}\n`;
+                } else if (parsed.type === "final_result") {
+                  refreshButton.title += ` ${parsed.message}`;
+                }
+              } catch (e) {
+                console.warn("Failed to parse message:", message, e);
+              }
+            }
+          }
+        } finally {
+          reader.releaseLock();
+        }
+      }
+
+      if (DOM.schemaContainer && DOM.schemaContainer.classList.contains("open")) {
+        await loadAndShowGraph(selected);
+        setTimeout(resizeGraph, 450);
+      }
+    } finally {
+      refreshButton.classList.remove("loading");
+      refreshButton.title = "";
+    }
   });

Also applies to: 149-151

🧹 Nitpick comments (5)
app/templates/components/chat_header.j2 (1)

6-6: A11y nit: reflect disabled state for screen readers

Consider adding aria-disabled="true" and a disabled style class to match the visual/interactive state until it’s re-enabled by JS.

-        <button class="action-button" id="graph-select-refresh" disabled>
+        <button class="action-button is-disabled" id="graph-select-refresh" disabled aria-disabled="true">
api/memory/graphiti_tool.py (2)

150-258: Blocking calls inside async and broad excepts

  • Config.EMBEDDING_MODEL.embed(...) is synchronous and may block the event loop. Consider running in a thread executor or switching to an async embedder if available.
  • Multiple bare except Exception; narrow where feasible or at least log with structured logging.

I can wire an async embedding utility (loop.run_in_executor) and replace prints with logging.getLogger(name).exception(...). Want a patch?


416-429: Use limit variable for vector query

Hardcoded 10 for topK fetches more than necessary.

-                    CALL db.idx.vector.queryNodes('Query', 'embeddings', 10, vecf32($embedding))
+                    CALL db.idx.vector.queryNodes('Query', 'embeddings', $topK, vecf32($embedding))
...
-                records, header, _  = await graph_driver.execute_query(cypher_query, embedding=query_embedding, database_node_uuid=database_node_uuid)
+                records, header, _  = await graph_driver.execute_query(
+                    cypher_query,
+                    embedding=query_embedding,
+                    database_node_uuid=database_node_uuid,
+                    topK=limit,
+                )
app/ts/app.ts (1)

81-88: Guard is fine; avoid hard-coded placeholder string.

"Select Database" is duplicated across files and is brittle if the label changes. Export a constant (e.g., SELECT_DB_PLACEHOLDER) from graph_select/config and reuse it here.

app/ts/modules/chat.ts (1)

14-15: Guard is fine; centralize the placeholder string.

Same string literal as in app.ts. Export and reuse a single SELECT_DB_PLACEHOLDER to avoid drift.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f6e7088 and 93766bc.

📒 Files selected for processing (6)
  • api/memory/graphiti_tool.py (9 hunks)
  • app/templates/components/chat_header.j2 (1 hunks)
  • app/templates/components/chat_input.j2 (1 hunks)
  • app/ts/app.ts (1 hunks)
  • app/ts/modules/chat.ts (1 hunks)
  • app/ts/modules/graph_select.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Frontend source changes in app/**/*.{ts,tsx} must be compiled before production runs

Files:

  • app/ts/app.ts
  • app/ts/modules/graph_select.ts
  • app/ts/modules/chat.ts
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

All Python code should pass pylint (use make lint)

Files:

  • api/memory/graphiti_tool.py
🧬 Code graph analysis (3)
app/ts/modules/graph_select.ts (1)
app/ts/modules/config.ts (1)
  • DOM (39-65)
app/ts/modules/chat.ts (2)
app/ts/modules/config.ts (1)
  • state (73-77)
app/ts/modules/messages.ts (1)
  • addMessage (8-118)
api/memory/graphiti_tool.py (1)
api/config.py (2)
  • Config (49-135)
  • embed (20-33)
🪛 Ruff (0.12.2)
api/memory/graphiti_tool.py

63-63: Unused class method argument: use_direct_entities

(ARG003)


249-249: Do not catch blind exception: Exception

(BLE001)


253-253: Consider moving this statement to an else block

(TRY300)


255-255: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests
  • GitHub Check: e2e-tests
  • GitHub Check: e2e-tests
🔇 Additional comments (2)
app/templates/components/chat_input.j2 (1)

4-4: Re-enable logic already implemented

The message input is re-enabled in app/ts/modules/chat.ts (line 254) and app/ts/modules/graphs.ts (line 126), so it will be enabled after graph selection. The original concern can be dismissed.

Likely an incorrect or invalid review comment.

api/memory/graphiti_tool.py (1)

380-439: Verify Graphiti.search_ calls return Entity nodes*

After switching to direct Entity creation, these search_/search calls may not surface Entity nodes unless Graphiti indexes the Entity label and its name_embedding. Confirm against a live Graphiti instance; if they don’t return the expected nodes, replace each lookup with a direct Cypher MATCH by label/name or UUID (as done in save_query_memory).

Affected locations in api/memory/graphiti_tool.py:

  • lines 85–87 (initial node search)
  • lines 122–124 (database node lookup)
  • lines 397–400 (retrieve_similar_queries DB node search)
  • lines 459–463 (user node search)
  • lines 491–495 (database node search)
  • lines 507–511 (reranked results search)

Comment thread app/ts/modules/chat.ts
Comment on lines +20 to 21
addMessage(message, "user", false, (window as any).currentUser || null);
if (DOM.messageInput) DOM.messageInput.value = '';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure all addMessage call sites use the updated argument order.

This call is correct (3rd arg isQuery=false, 4th user). There are older call sites passing the user object as the 3rd arg, which drops the avatar. Verify and align.

Run to find suspicious 3-arg "user" calls:

If matches exist, update them to addMessage(msg, "user", false, currentUser).


🏁 Script executed:

#!/bin/bash
# 3-arg addMessage with "user" as 2nd arg (likely missing the boolean third arg)
rg -nP --type=ts -C2 '\baddMessage\s*\(\s*[^,]+,\s*"user"\s*,\s*[^,\)]+\s*\)'

Length of output: 407


Update the 3-arg addMessage call on line 257 to include the isQuery boolean.

--- a/app/ts/modules/chat.ts
+++ b/app/ts/modules/chat.ts
@@ 255,262
-   addMessage(`User choice: ${confirmation}`, "user", (window as any).currentUser || null);
+   addMessage(`User choice: ${confirmation}`, "user", false, (window as any).currentUser || null);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
addMessage(message, "user", false, (window as any).currentUser || null);
if (DOM.messageInput) DOM.messageInput.value = '';
++ b/app/ts/modules/chat.ts
@@ -255,7 +255,7 @@
// … other logic …
addMessage(`User choice: ${confirmation}`, "user", false, (window as any).currentUser || null);
// … following logic …
🤖 Prompt for AI Agents
In app/ts/modules/chat.ts around lines 20 to 21, the addMessage invocation must
include the isQuery boolean parameter; modify the call to pass the appropriate
isQuery value (insert the boolean as the third argument) so the remaining
arguments are shifted accordingly (e.g., addMessage(message, "user", /*isQuery*/
false, /*other args*/ (window as any).currentUser || null)); ensure the argument
order matches addMessage's signature.

Comment on lines +46 to +49
if (DOM.graphSelectRefresh && DOM.submitButton) {
DOM.graphSelectRefresh.disabled = false
DOM.submitButton.disabled = false
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Also enable the message input; enable controls independently

Currently only refresh and submit are enabled, and only if both elements exist. Enable each control independently and re-enable the input so users can type immediately.

-        if (DOM.graphSelectRefresh && DOM.submitButton) {
-            DOM.graphSelectRefresh.disabled = false
-            DOM.submitButton.disabled = false
-        };
+        if (DOM.graphSelectRefresh) {
+            DOM.graphSelectRefresh.disabled = false;
+            DOM.graphSelectRefresh.removeAttribute('aria-disabled');
+        }
+        if (DOM.submitButton) {
+            DOM.submitButton.disabled = false;
+        }
+        if (DOM.messageInput) {
+            DOM.messageInput.disabled = false;
+            DOM.messageInput.focus();
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (DOM.graphSelectRefresh && DOM.submitButton) {
DOM.graphSelectRefresh.disabled = false
DOM.submitButton.disabled = false
};
// Re-enable the graph-select refresh button if it exists
if (DOM.graphSelectRefresh) {
DOM.graphSelectRefresh.disabled = false;
DOM.graphSelectRefresh.removeAttribute('aria-disabled');
}
// Re-enable the submit button if it exists
if (DOM.submitButton) {
DOM.submitButton.disabled = false;
}
// Re-enable and focus the message input so users can type immediately
if (DOM.messageInput) {
DOM.messageInput.disabled = false;
DOM.messageInput.focus();
}
🤖 Prompt for AI Agents
In app/ts/modules/graph_select.ts around lines 46 to 49, the code only enables
refresh and submit when both elements exist and neglects the message input;
change it to enable each control independently by checking each DOM element
separately (if DOM.graphSelectRefresh then set disabled = false; if
DOM.submitButton then set disabled = false; if DOM.messageInput then set
disabled = false and call focus() so users can type immediately), ensuring each
check is independent rather than combined.

@galshubeli galshubeli changed the base branch from main to staging September 3, 2025 10:37
@galshubeli galshubeli closed this Sep 3, 2025
gkorland pushed a commit that referenced this pull request Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants