Skip to content

Python: samples: add Synap long-term memory plugin sample#14008

Open
visy-ani wants to merge 2 commits into
microsoft:mainfrom
visy-ani:add-synap-memory-sample
Open

Python: samples: add Synap long-term memory plugin sample#14008
visy-ani wants to merge 2 commits into
microsoft:mainfrom
visy-ani:add-synap-memory-sample

Conversation

@visy-ani
Copy link
Copy Markdown

Summary

Adds python/samples/concepts/memory/synap_memory.py — a sample showing how to give a Semantic Kernel agent persistent, cross-session memory via Synap.

The sample registers SynapPlugin (from synap-semantic-kernel) which exposes two kernel functions:

  • synap-search_memory — semantic search over the user's stored memories
  • synap-store_memory — persist explicit facts

With FunctionChoiceBehavior.Auto(), the chat completion service can call these functions on demand to remember and recall facts about the user.

PyPI: https://pypi.org/project/synap-semantic-kernel/
Open source: https://github.com/maximem-ai/maximem_synap_sdk/tree/main/packages/integrations/synap-semantic-kernel

Contribution Checklist

Copilot AI review requested due to automatic review settings May 14, 2026 11:06
@visy-ani visy-ani requested a review from a team as a code owner May 14, 2026 11:06
@moonbox3 moonbox3 added the python Pull requests for the Python Semantic Kernel label May 14, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 91%

✓ Correctness

This is a clean, well-structured sample that correctly uses the Semantic Kernel APIs (Kernel, ChatHistory, FunctionChoiceBehavior.Auto, get_chat_message_content). The only minor correctness concern is that get_chat_message_content returns ChatMessageContent | None (chat_completion_client_base.py:178), and the sample passes response directly to history.add_message() on line 66 without a None guard. If response were None, add_message would raise a TypeError because it checks isinstance(message, ChatMessageContent) then "role" not in message (chat_history.py:205-208), and in doesn't work on NoneType. In practice, the None return path is marked pragma: no cover with a comment saying 'this should not happen' (chat_completion_client_base.py:193-194), and some other samples (e.g., crew_ai_plugin.py:93-94) also skip this check. So this is non-blocking but worth noting.

✓ Security Reliability

This is a straightforward sample file demonstrating a third-party Synap memory plugin. No security or reliability issues were found. The sample follows existing conventions: API keys are sourced from environment variables (no hardcoded secrets), demo-only identifiers are clearly labeled, and the code structure mirrors other memory samples in the directory. The one minor deviation from repo convention is using bare os.environ[] for the Synap API key (other samples delegate env-var resolution to SDK constructors via Pydantic settings), but this is dictated by the third-party SDK's interface and is not a security or reliability concern.

✓ Test Coverage

This PR adds a new concept sample for Synap long-term memory. Regarding test coverage: the repository convention is that most concept samples—especially those requiring external service credentials—are not tested in test_concepts.py. Of the existing memory samples, only simple_memory.py is tested; complex_memory.py and memory_with_pandas.py are not (they also depend on external services). Since synap_memory.py requires both SYNAP_API_KEY and OPENAI_API_KEY plus third-party packages (maximem_synap, synap_semantic_kernel), skipping integration tests is consistent with existing patterns. However, the sample is not listed in the concepts README.md, which every other memory sample is.

✗ Design Approach

The sample’s overall shape is fine, but it currently does not demonstrate the cross-session memory behavior described in the PR. The second turn reuses the same ChatHistory, so the model can answer from short-term conversation state even if the Synap plugin never stores or retrieves anything.

Flagged Issues

  • The sample keeps the user's fact in the same ChatHistory across both turns (lines 60-72), so the recall question can be answered from short-term chat context alone—without exercising Synap's persistent memory path at all. This means the sample does not actually demonstrate the cross-session memory feature it introduces.

Automated review by visy-ani's agents

chat_history=history, settings=settings, kernel=kernel
)
print(f"Assistant: {response}")
history.add_message(response)
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.

Nitpick: get_chat_message_content returns ChatMessageContent | None. If response is None, add_message will raise TypeError. The None path is effectively dead code today (marked pragma: no cover) and other samples also skip this guard, so this is non-blocking—but adding a guard would be more defensive.

Suggested change
history.add_message(response)
if response:
history.add_message(response)

Comment on lines +69 to +74
history.add_user_message("What do you know about my dietary restrictions?")
response = await chat_service.get_chat_message_content(
chat_history=history, settings=settings, kernel=kernel
)
print(f"Assistant: {response}")

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.

The PR describes persistent cross-session memory, but history still contains the user's original fact from the first turn. The model can answer from short-term chat history even if synap-store_memory/synap-search_memory are never invoked. Re-create the ChatHistory before the recall turn to actually exercise the long-term memory path.

Suggested change
history.add_user_message("What do you know about my dietary restrictions?")
response = await chat_service.get_chat_message_content(
chat_history=history, settings=settings, kernel=kernel
)
print(f"Assistant: {response}")
# Turn 2: recall from a fresh session so Synap, not short-term chat history, supplies the fact
history = ChatHistory()
history.add_system_message(
"You are a helpful assistant with long-term memory. "
"Use synap-search_memory to recall facts about the user. "
"Use synap-store_memory to save important new facts."
)
history.add_user_message("What do you know about my dietary restrictions?")
response = await chat_service.get_chat_message_content(
chat_history=history, settings=settings, kernel=kernel
)
print(f"Assistant: {response}")

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

Adds a Python concept sample demonstrating Synap-backed long-term memory for Semantic Kernel chat completion with automatic function calling.

Changes:

  • Adds setup documentation for installing Synap dependencies and configuring API keys.
  • Initializes Synap SDK, registers SynapPlugin, and enables FunctionChoiceBehavior.Auto().
  • Demonstrates a two-turn store/recall memory flow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


# Turn 1: teach
history.add_user_message(
"I'm a software engineer who's allergic to peanuts. Remember this."
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(ai_model_id="gpt-4o-mini"))
kernel.add_plugin(
SynapPlugin(sdk=sdk, user_id="demo-user-001", customer_id="demo-customer"),
@@ -0,0 +1,77 @@
# Copyright (c) Microsoft. All rights reserved.

"""Sample: long-term memory via the Synap plugin.
@visy-ani
Copy link
Copy Markdown
Author

Hi @moonbox3 and @westey-m 👋 — would love a review when you have a moment! This adds python/samples/concepts/memory/synap_memory.py showing how to plug long-term memory into Semantic Kernel via Synap.

Why this fits concepts/memory/: the sample registers a SynapPlugin whose two @kernel_function methods (search_memory, store_memory) are picked up automatically by FunctionChoiceBehavior.Auto(). It demonstrates the kernel-function pattern end-to-end with a real persistence backend, which complements the existing in-memory and vector-store samples already in that folder.

All real CI checks are green (CLA, Agent, Prepare, review, add_label, title prefix). The "Cleanup artifacts" failure is the standard post-job cleanup. Thanks!

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

Labels

python Pull requests for the Python Semantic Kernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants