Skip to content

feat: support seperate memory - #3

Closed
jaffrey-deepsource wants to merge 2 commits into
mainfrom
issue-13738
Closed

feat: support seperate memory#3
jaffrey-deepsource wants to merge 2 commits into
mainfrom
issue-13738

Conversation

@jaffrey-deepsource

Copy link
Copy Markdown
Collaborator

No description provided.

@github-actions github-actions Bot added the web label Feb 3, 2026
@jaffrey-deepsource jaffrey-deepsource changed the title Issue 13738 feat: support seperate memory Feb 3, 2026
@deepsource-development

deepsource-development Bot commented Feb 3, 2026

Copy link
Copy Markdown

Here's the code health analysis summary for commits b76c8fa..1df0167. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource Python LogoPython❌ Failure
❗ 7 occurences introduced
🎯 4 occurences resolved
View Check ↗
DeepSource Secrets LogoSecrets✅ SuccessView Check ↗

DeepSource Report Card: B

DimensionGradeIssues
SecurityA0
ReliabilityC⚠️
ComplexityA0
HygieneB2

Focus area: Reliability — Fix the double application of prefixes corrupting the prompt history format in `api/core/memory/node_scoped_memory.py`.

View full report →


💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@deepsource-development deepsource-development Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

DeepSource detected 8 newly introduced issue(s) in this pull request.

@@ -8,6 +8,7 @@
from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
from core.entities.provider_entities import QuotaUnit
from core.file.models import File

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused File import increases code clutter

The File object imported from core.file.models is not used anywhere in the module, which adds unnecessary code clutter and can confuse maintainers. This can also slightly affect code readability and static analysis.
Remove the unused File import statement to clean up and simplify the codebase.

) -> str: ...


class LLMNode(Node):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unimplemented @abstractmethod risks incomplete class behavior

The class LLMNode inherits from Node but does not override all methods marked with @abstractmethod in the base class. This omission can lead to runtime errors or unintended behavior when abstract contracts are not fulfilled.

Implement all abstract methods defined in Node in the LLMNode class to ensure complete and correct behavior.

self.credentials = {}

class _FakeManager:
def get_model_instance(self, *args, **kwargs):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Method without self usage wastes instance memory

The method get_model_instance does not use the self parameter, so it does not depend on the instance state. Binding this method as an instance method creates unnecessary overhead for each class instance.

Add the @staticmethod decorator before the method definition to avoid binding it to instances, conserving memory and reducing call overhead.

monkeypatch.setattr(LLMNode, "_run", _fake_run)

# Run node
events = list(llm_node._run())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused variable events occupies memory unnecessarily

The variable events is assigned the list from llm_node._run() but never used, leading to unnecessary memory consumption and potential confusion for maintainers. This appears on line 267.
Remove the assignment of events if the result is not needed, or rename it to _ or start with _unused to explicitly mark it as intentionally unused.

# Create a fake invoke_llm that just produces a completion event
def _fake_invoke_llm_simple(self, **kwargs):
from core.model_runtime.entities.llm_entities import LLMUsage
from core.workflow.node_events.node import ModelInvokeCompletedEvent

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Repeated import of ModelInvokeCompletedEvent causes confusion

The ModelInvokeCompletedEvent is imported multiple times within the same file or context. This duplication can confuse maintainers and lead to inconsistent usage or accidental shadowing.

Remove redundant imports of ModelInvokeCompletedEvent and consolidate imports to a single statement to maintain code clarity and reduce potential bugs.

Comment on lines +180 to +208
prefix = human_prefix if role_name == PromptMessageRole.USER else ai_prefix
messages.append(
UserPromptMessage(content=f"{prefix}: {it.text}")
if role_name == PromptMessageRole.USER
else AssistantPromptMessage(content=f"{prefix}: {it.text}")
)

if messages:
tokens = self.model_instance.get_llm_num_tokens(messages)
while tokens > max_token_limit and len(messages) > 1:
messages.pop(0)
tokens = self.model_instance.get_llm_num_tokens(messages)

# Convert back to the required text format
lines: list[str] = []
for m in messages:
if m.role == PromptMessageRole.USER:
prefix = human_prefix
elif m.role == PromptMessageRole.ASSISTANT:
prefix = ai_prefix
else:
continue
if isinstance(m.content, list):
# Only text content was saved in this minimal implementation
texts = [c.data for c in m.content if isinstance(c, TextPromptMessageContent)]
text = "\n".join(texts)
else:
text = str(m.content)
lines.append(f"{prefix}: {text}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Prefixes are applied twice to history text

The method get_history_prompt_text incorrectly applies the human_prefix and ai_prefix to the message content twice. The prefix is first added when creating PromptMessage objects for token counting, and then it is added again when formatting the final string output, resulting in erroneous output like Human: Human: some user text.

Remove the second prefix application from the final loop that generates the text lines. The message content, which is converted to text, already contains the necessary prefix.

Comment on lines +323 to +324
if independent_scope and node_memory:
node_memory.clear()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Node-scoped memory is cleared regardless of the clear_after_execution setting

The code checks if independent_scope is enabled and clears node_memory if it exists. However, it fails to check the clear_after_execution flag from the node's memory configuration, causing data loss for users who expect memory to persist.

Add a condition to check self._node_data.memory.clear_after_execution before calling node_memory.clear().

@deepsource-development deepsource-development Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

DeepSource detected 1 newly introduced issue(s) in this pull request.

Comment on lines +182 to +208
UserPromptMessage(content=f"{prefix}: {it.text}")
if role_name == PromptMessageRole.USER
else AssistantPromptMessage(content=f"{prefix}: {it.text}")
)

if messages:
tokens = self.model_instance.get_llm_num_tokens(messages)
while tokens > max_token_limit and len(messages) > 1:
messages.pop(0)
tokens = self.model_instance.get_llm_num_tokens(messages)

# Convert back to the required text format
lines: list[str] = []
for m in messages:
if m.role == PromptMessageRole.USER:
prefix = human_prefix
elif m.role == PromptMessageRole.ASSISTANT:
prefix = ai_prefix
else:
continue
if isinstance(m.content, list):
# Only text content was saved in this minimal implementation
texts = [c.data for c in m.content if isinstance(c, TextPromptMessageContent)]
text = "\n".join(texts)
else:
text = str(m.content)
lines.append(f"{prefix}: {text}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Prefixes are applied twice, corrupting prompt history format

The get_history_prompt_text method incorrectly applies prefixes to history items twice. It first adds a prefix when creating PromptMessage objects, and then adds the same prefix again when converting these messages back to a text format, leading to corrupted output like Human: Human: ....

Remove the second prefixing step. The content of the PromptMessage object already contains the required prefix and text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants