maximem-synap-microsoft-agent 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: maximem-synap-microsoft-agent
3
+ Version: 0.1.1
4
+ Summary: Synap memory integration for Microsoft Agent Framework
5
+ Author: Synap Team
6
+ License-Expression: Apache-2.0
7
+ Keywords: synap,memory,microsoft,agent-framework,ai,agents
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: maximem-synap>=0.2.0
11
+ Requires-Dist: synap-integrations-common>=0.1.0
12
+ Requires-Dist: agent-framework>=1.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=7.0; extra == "dev"
15
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
16
+
17
+ # synap-microsoft-agent
18
+
19
+ Synap memory integration for [Microsoft Agent Framework (MAF)](https://learn.microsoft.com/en-us/agent-framework/).
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install synap-microsoft-agent
25
+ ```
26
+
27
+ Requires `agent-framework>=1.0`, `maximem-synap>=0.2.0`.
28
+
29
+ ## Quickstart
30
+
31
+ ```python
32
+ from agent_framework import InMemoryHistoryProvider
33
+ from agent_framework.openai import OpenAIChatClient
34
+ from maximem_synap import MaximemSynapSDK
35
+ from synap_microsoft_agent import SynapContextProvider, SynapHistoryProvider
36
+
37
+ sdk = MaximemSynapSDK(api_key="sk-...")
38
+ client = OpenAIChatClient(model="gpt-4o-mini")
39
+
40
+ agent = client.as_agent(
41
+ name="MemoryAgent",
42
+ instructions="You are a helpful assistant.",
43
+ context_providers=[
44
+ SynapContextProvider(
45
+ sdk=sdk,
46
+ user_id="alice",
47
+ customer_id="acme",
48
+ ),
49
+ SynapHistoryProvider(
50
+ sdk=sdk,
51
+ user_id="alice",
52
+ customer_id="acme",
53
+ ),
54
+ ],
55
+ )
56
+
57
+ session = agent.create_session()
58
+ result = await agent.run("What's my trial expiring?", session=session)
59
+ ```
60
+
61
+ ## What each provider does
62
+
63
+ - **`SynapContextProvider`** — on every turn, fetches Synap context (facts, preferences, episodes, emotions, temporal events) and appends it as instructions. After the turn, records the user + assistant messages back to Synap.
64
+
65
+ - **`SynapHistoryProvider`** — persists the conversation message log. Loads prior turns on session resume. Subclass of MAF's `HistoryProvider`, so all its flags (`load_messages`, `store_inputs`, `store_outputs`, `store_context_messages`) work as documented.
66
+
67
+ You can use either or both; they coexist.
68
+
69
+ ## Error policy
70
+
71
+ - **Read-side failures** (`fetch`, `get_context_for_prompt`) degrade gracefully — logged at `ERROR`, empty result returned. An outage never crashes an agent turn.
72
+ - **Write-side failures** — `SynapContextProvider.after_run` logs and swallows (context providers must not raise per MAF's hook contract). `SynapHistoryProvider.save_messages` surfaces errors as `SynapIntegrationError` so explicit persistence failures are observable.
@@ -0,0 +1,56 @@
1
+ # synap-microsoft-agent
2
+
3
+ Synap memory integration for [Microsoft Agent Framework (MAF)](https://learn.microsoft.com/en-us/agent-framework/).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install synap-microsoft-agent
9
+ ```
10
+
11
+ Requires `agent-framework>=1.0`, `maximem-synap>=0.2.0`.
12
+
13
+ ## Quickstart
14
+
15
+ ```python
16
+ from agent_framework import InMemoryHistoryProvider
17
+ from agent_framework.openai import OpenAIChatClient
18
+ from maximem_synap import MaximemSynapSDK
19
+ from synap_microsoft_agent import SynapContextProvider, SynapHistoryProvider
20
+
21
+ sdk = MaximemSynapSDK(api_key="sk-...")
22
+ client = OpenAIChatClient(model="gpt-4o-mini")
23
+
24
+ agent = client.as_agent(
25
+ name="MemoryAgent",
26
+ instructions="You are a helpful assistant.",
27
+ context_providers=[
28
+ SynapContextProvider(
29
+ sdk=sdk,
30
+ user_id="alice",
31
+ customer_id="acme",
32
+ ),
33
+ SynapHistoryProvider(
34
+ sdk=sdk,
35
+ user_id="alice",
36
+ customer_id="acme",
37
+ ),
38
+ ],
39
+ )
40
+
41
+ session = agent.create_session()
42
+ result = await agent.run("What's my trial expiring?", session=session)
43
+ ```
44
+
45
+ ## What each provider does
46
+
47
+ - **`SynapContextProvider`** — on every turn, fetches Synap context (facts, preferences, episodes, emotions, temporal events) and appends it as instructions. After the turn, records the user + assistant messages back to Synap.
48
+
49
+ - **`SynapHistoryProvider`** — persists the conversation message log. Loads prior turns on session resume. Subclass of MAF's `HistoryProvider`, so all its flags (`load_messages`, `store_inputs`, `store_outputs`, `store_context_messages`) work as documented.
50
+
51
+ You can use either or both; they coexist.
52
+
53
+ ## Error policy
54
+
55
+ - **Read-side failures** (`fetch`, `get_context_for_prompt`) degrade gracefully — logged at `ERROR`, empty result returned. An outage never crashes an agent turn.
56
+ - **Write-side failures** — `SynapContextProvider.after_run` logs and swallows (context providers must not raise per MAF's hook contract). `SynapHistoryProvider.save_messages` surfaces errors as `SynapIntegrationError` so explicit persistence failures are observable.
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: maximem-synap-microsoft-agent
3
+ Version: 0.1.1
4
+ Summary: Synap memory integration for Microsoft Agent Framework
5
+ Author: Synap Team
6
+ License-Expression: Apache-2.0
7
+ Keywords: synap,memory,microsoft,agent-framework,ai,agents
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: maximem-synap>=0.2.0
11
+ Requires-Dist: synap-integrations-common>=0.1.0
12
+ Requires-Dist: agent-framework>=1.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=7.0; extra == "dev"
15
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
16
+
17
+ # synap-microsoft-agent
18
+
19
+ Synap memory integration for [Microsoft Agent Framework (MAF)](https://learn.microsoft.com/en-us/agent-framework/).
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install synap-microsoft-agent
25
+ ```
26
+
27
+ Requires `agent-framework>=1.0`, `maximem-synap>=0.2.0`.
28
+
29
+ ## Quickstart
30
+
31
+ ```python
32
+ from agent_framework import InMemoryHistoryProvider
33
+ from agent_framework.openai import OpenAIChatClient
34
+ from maximem_synap import MaximemSynapSDK
35
+ from synap_microsoft_agent import SynapContextProvider, SynapHistoryProvider
36
+
37
+ sdk = MaximemSynapSDK(api_key="sk-...")
38
+ client = OpenAIChatClient(model="gpt-4o-mini")
39
+
40
+ agent = client.as_agent(
41
+ name="MemoryAgent",
42
+ instructions="You are a helpful assistant.",
43
+ context_providers=[
44
+ SynapContextProvider(
45
+ sdk=sdk,
46
+ user_id="alice",
47
+ customer_id="acme",
48
+ ),
49
+ SynapHistoryProvider(
50
+ sdk=sdk,
51
+ user_id="alice",
52
+ customer_id="acme",
53
+ ),
54
+ ],
55
+ )
56
+
57
+ session = agent.create_session()
58
+ result = await agent.run("What's my trial expiring?", session=session)
59
+ ```
60
+
61
+ ## What each provider does
62
+
63
+ - **`SynapContextProvider`** — on every turn, fetches Synap context (facts, preferences, episodes, emotions, temporal events) and appends it as instructions. After the turn, records the user + assistant messages back to Synap.
64
+
65
+ - **`SynapHistoryProvider`** — persists the conversation message log. Loads prior turns on session resume. Subclass of MAF's `HistoryProvider`, so all its flags (`load_messages`, `store_inputs`, `store_outputs`, `store_context_messages`) work as documented.
66
+
67
+ You can use either or both; they coexist.
68
+
69
+ ## Error policy
70
+
71
+ - **Read-side failures** (`fetch`, `get_context_for_prompt`) degrade gracefully — logged at `ERROR`, empty result returned. An outage never crashes an agent turn.
72
+ - **Write-side failures** — `SynapContextProvider.after_run` logs and swallows (context providers must not raise per MAF's hook contract). `SynapHistoryProvider.save_messages` surfaces errors as `SynapIntegrationError` so explicit persistence failures are observable.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ maximem_synap_microsoft_agent.egg-info/PKG-INFO
4
+ maximem_synap_microsoft_agent.egg-info/SOURCES.txt
5
+ maximem_synap_microsoft_agent.egg-info/dependency_links.txt
6
+ maximem_synap_microsoft_agent.egg-info/requires.txt
7
+ maximem_synap_microsoft_agent.egg-info/top_level.txt
8
+ synap_microsoft_agent/__init__.py
9
+ synap_microsoft_agent/context_provider.py
10
+ synap_microsoft_agent/history_provider.py
@@ -0,0 +1,7 @@
1
+ maximem-synap>=0.2.0
2
+ synap-integrations-common>=0.1.0
3
+ agent-framework>=1.0
4
+
5
+ [dev]
6
+ pytest>=7.0
7
+ pytest-asyncio>=0.21
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "maximem-synap-microsoft-agent"
7
+ version = "0.1.1"
8
+ description = "Synap memory integration for Microsoft Agent Framework"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "Apache-2.0"
12
+ authors = [
13
+ {name = "Synap Team"}
14
+ ]
15
+ keywords = ["synap", "memory", "microsoft", "agent-framework", "ai", "agents"]
16
+
17
+ dependencies = [
18
+ "maximem-synap>=0.2.0",
19
+ "synap-integrations-common>=0.1.0",
20
+ "agent-framework>=1.0",
21
+ ]
22
+
23
+ [project.optional-dependencies]
24
+ dev = [
25
+ "pytest>=7.0",
26
+ "pytest-asyncio>=0.21",
27
+ ]
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["."]
31
+ include = ["synap_microsoft_agent*"]
32
+
33
+ [tool.pytest.ini_options]
34
+ testpaths = ["tests"]
35
+ asyncio_mode = "auto"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,32 @@
1
+ """Synap integration for Microsoft Agent Framework (MAF).
2
+
3
+ Two plug points into MAF's context-engineering pipeline:
4
+
5
+ - :class:`SynapContextProvider` — subclass of ``ContextProvider``. Injects
6
+ Synap-sourced context into the agent's instructions on every turn via
7
+ ``before_run``, and records each turn back to Synap via ``after_run``.
8
+ Pattern mirrors ``agent_framework.mem0.Mem0ContextProvider``.
9
+
10
+ - :class:`SynapHistoryProvider` — subclass of ``HistoryProvider``. Persists
11
+ the conversation message log to Synap via ``sdk.conversation.record_message``
12
+ and loads it back via ``sdk.conversation.context.get_context_for_prompt``.
13
+
14
+ Typical wiring::
15
+
16
+ from agent_framework import InMemoryHistoryProvider
17
+ from synap_microsoft_agent import SynapContextProvider
18
+
19
+ agent = client.as_agent(
20
+ name="MemoryAgent",
21
+ instructions="You are a helpful assistant.",
22
+ context_providers=[
23
+ SynapContextProvider(sdk=sdk, user_id="alice", customer_id="acme"),
24
+ InMemoryHistoryProvider(load_messages=True),
25
+ ],
26
+ )
27
+ """
28
+
29
+ from synap_microsoft_agent.context_provider import SynapContextProvider
30
+ from synap_microsoft_agent.history_provider import SynapHistoryProvider
31
+
32
+ __all__ = ["SynapContextProvider", "SynapHistoryProvider"]
@@ -0,0 +1,187 @@
1
+ """SynapContextProvider — MAF ContextProvider backed by Synap.
2
+
3
+ Pattern mirrors ``agent_framework.mem0.Mem0ContextProvider``:
4
+
5
+ - ``before_run`` builds a query from ``context.input_messages`` text, calls
6
+ ``sdk.fetch(...)``, and appends the formatted result as instructions via
7
+ ``context.extend_instructions(source_id, ...)``. Read failures degrade
8
+ gracefully — we log + skip rather than breaking the agent turn.
9
+
10
+ - ``after_run`` records each input + response message to Synap via
11
+ ``sdk.conversation.record_message(...)``. Write failures are logged but
12
+ never re-raised, following MAF's "context providers should not crash the
13
+ agent" contract (same as LangChain's ``SynapCallbackHandler``).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ from typing import Any, ClassVar, Optional
20
+
21
+ from agent_framework import ContextProvider
22
+ from maximem_synap import MaximemSynapSDK
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class SynapContextProvider(ContextProvider):
28
+ """Inject Synap context + record turns back to Synap."""
29
+
30
+ DEFAULT_SOURCE_ID: ClassVar[str] = "synap"
31
+ DEFAULT_CONTEXT_PROMPT: ClassVar[str] = (
32
+ "## User Memory Context\n"
33
+ "Consider the following context about the user and their history "
34
+ "when answering. Treat it as background knowledge, not direct input."
35
+ )
36
+
37
+ def __init__(
38
+ self,
39
+ sdk: MaximemSynapSDK,
40
+ user_id: str,
41
+ customer_id: str = "",
42
+ conversation_id: Optional[str] = None,
43
+ *,
44
+ source_id: str = DEFAULT_SOURCE_ID,
45
+ mode: str = "accurate",
46
+ max_results: int = 20,
47
+ context_prompt: Optional[str] = None,
48
+ include_scope_labels: bool = False,
49
+ ) -> None:
50
+ if sdk is None:
51
+ raise ValueError("SynapContextProvider requires a non-None sdk")
52
+ if not user_id:
53
+ raise ValueError("SynapContextProvider requires a non-empty user_id")
54
+
55
+ super().__init__(source_id)
56
+ self.sdk = sdk
57
+ self.user_id = user_id
58
+ self.customer_id = customer_id
59
+ self.conversation_id = conversation_id
60
+ self.mode = mode
61
+ self.max_results = max_results
62
+ self.context_prompt = context_prompt or self.DEFAULT_CONTEXT_PROMPT
63
+ self.include_scope_labels = include_scope_labels
64
+
65
+ async def before_run(
66
+ self,
67
+ *,
68
+ agent: Any,
69
+ session: Any,
70
+ context: Any,
71
+ state: dict[str, Any],
72
+ ) -> None:
73
+ """Fetch Synap context and append it to the agent's instructions."""
74
+ input_text = self._concat_text(context.input_messages)
75
+ if not input_text:
76
+ return
77
+
78
+ conv_id = self._resolve_conversation_id(context)
79
+
80
+ try:
81
+ response = await self.sdk.fetch(
82
+ conversation_id=conv_id,
83
+ user_id=self.user_id,
84
+ customer_id=self.customer_id or None,
85
+ search_query=[input_text],
86
+ max_results=self.max_results,
87
+ mode=self.mode,
88
+ include_conversation_context=False,
89
+ include_scope_labels=self.include_scope_labels,
90
+ )
91
+ except Exception as exc: # noqa: BLE001 — read-side degrades gracefully
92
+ logger.error(
93
+ "SynapContextProvider.before_run: sdk.fetch failed "
94
+ "user_id=%s conversation_id=%s error=%s",
95
+ self.user_id,
96
+ conv_id,
97
+ exc,
98
+ exc_info=True,
99
+ )
100
+ return
101
+
102
+ formatted = (response.formatted_context or "").strip()
103
+ if not formatted:
104
+ return
105
+
106
+ context.extend_instructions(
107
+ self.source_id,
108
+ f"{self.context_prompt}\n{formatted}",
109
+ )
110
+
111
+ async def after_run(
112
+ self,
113
+ *,
114
+ agent: Any,
115
+ session: Any,
116
+ context: Any,
117
+ state: dict[str, Any],
118
+ ) -> None:
119
+ """Record input + response messages to Synap conversation history."""
120
+ conv_id = self._resolve_conversation_id(context)
121
+ if not conv_id:
122
+ # Without a conversation id we can't attribute the turn — skip.
123
+ return
124
+
125
+ to_record: list[tuple[str, str]] = []
126
+ for message in context.input_messages or []:
127
+ role, text = self._message_to_role_text(message)
128
+ if role and text:
129
+ to_record.append((role, text))
130
+
131
+ response = context.response
132
+ if response is not None and getattr(response, "messages", None):
133
+ for message in response.messages:
134
+ role, text = self._message_to_role_text(message)
135
+ if role and text:
136
+ to_record.append((role, text))
137
+
138
+ for role, text in to_record:
139
+ try:
140
+ await self.sdk.conversation.record_message(
141
+ conversation_id=conv_id,
142
+ role=role,
143
+ content=text,
144
+ user_id=self.user_id,
145
+ customer_id=self.customer_id,
146
+ )
147
+ except Exception as exc: # noqa: BLE001 — must not crash the agent
148
+ logger.error(
149
+ "SynapContextProvider.after_run: record_message failed "
150
+ "role=%s conversation_id=%s error=%s",
151
+ role,
152
+ conv_id,
153
+ exc,
154
+ exc_info=True,
155
+ )
156
+
157
+ # -- helpers --------------------------------------------------------------
158
+
159
+ def _resolve_conversation_id(self, context: Any) -> Optional[str]:
160
+ if self.conversation_id:
161
+ return self.conversation_id
162
+ sid = getattr(context, "session_id", None)
163
+ return sid if sid else None
164
+
165
+ @staticmethod
166
+ def _concat_text(messages: Any) -> str:
167
+ if not messages:
168
+ return ""
169
+ parts: list[str] = []
170
+ for msg in messages:
171
+ text = getattr(msg, "text", None)
172
+ if text and text.strip():
173
+ parts.append(text.strip())
174
+ return "\n".join(parts)
175
+
176
+ @staticmethod
177
+ def _message_to_role_text(message: Any) -> tuple[Optional[str], str]:
178
+ role_raw = getattr(message, "role", None)
179
+ if role_raw is None:
180
+ return None, ""
181
+ role = role_raw.value if hasattr(role_raw, "value") else str(role_raw)
182
+ if role not in {"user", "assistant", "system"}:
183
+ return None, ""
184
+ text = getattr(message, "text", "") or ""
185
+ if not text.strip():
186
+ return None, ""
187
+ return role, text
@@ -0,0 +1,133 @@
1
+ """SynapHistoryProvider — MAF HistoryProvider backed by Synap.
2
+
3
+ Persists conversation messages to Synap and loads them back into the MAF
4
+ session. Wrap one or more of these in ``context_providers`` to get durable,
5
+ cross-process conversation memory.
6
+
7
+ Load path (``get_messages``) reads via ``sdk.conversation.context.get_context_for_prompt``
8
+ and degrades gracefully on failure (returns ``[]``) — a history read outage
9
+ shouldn't abort the agent turn.
10
+
11
+ Save path (``save_messages``) iterates ``sdk.conversation.record_message``
12
+ and surfaces SDK errors as :class:`SynapIntegrationError` because silent drops
13
+ would hide ingestion problems from the caller.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ from typing import Any, ClassVar, Optional, Sequence
20
+
21
+ from agent_framework import Message
22
+ from agent_framework._sessions import HistoryProvider
23
+ from maximem_synap import MaximemSynapSDK
24
+ from synap_integrations_common import wrap_sdk_errors_async
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class SynapHistoryProvider(HistoryProvider):
30
+ """Store + load MAF conversation history via Synap."""
31
+
32
+ DEFAULT_SOURCE_ID: ClassVar[str] = "synap_history"
33
+
34
+ def __init__(
35
+ self,
36
+ sdk: MaximemSynapSDK,
37
+ user_id: str,
38
+ customer_id: str = "",
39
+ conversation_id: Optional[str] = None,
40
+ *,
41
+ source_id: str = DEFAULT_SOURCE_ID,
42
+ load_messages: bool = True,
43
+ store_inputs: bool = True,
44
+ store_context_messages: bool = False,
45
+ store_context_from: Optional[set[str]] = None,
46
+ store_outputs: bool = True,
47
+ ) -> None:
48
+ if sdk is None:
49
+ raise ValueError("SynapHistoryProvider requires a non-None sdk")
50
+ if not user_id:
51
+ raise ValueError("SynapHistoryProvider requires a non-empty user_id")
52
+
53
+ super().__init__(
54
+ source_id,
55
+ load_messages=load_messages,
56
+ store_inputs=store_inputs,
57
+ store_context_messages=store_context_messages,
58
+ store_context_from=store_context_from,
59
+ store_outputs=store_outputs,
60
+ )
61
+ self.sdk = sdk
62
+ self.user_id = user_id
63
+ self.customer_id = customer_id
64
+ self.conversation_id = conversation_id
65
+
66
+ async def get_messages(
67
+ self,
68
+ session_id: Optional[str],
69
+ *,
70
+ state: Optional[dict[str, Any]] = None,
71
+ **kwargs: Any,
72
+ ) -> list[Message]:
73
+ conv_id = self.conversation_id or session_id
74
+ if not conv_id:
75
+ return []
76
+
77
+ try:
78
+ response = await self.sdk.conversation.context.get_context_for_prompt(
79
+ conversation_id=conv_id,
80
+ )
81
+ except Exception as exc: # noqa: BLE001 — read-side degrades gracefully
82
+ logger.error(
83
+ "SynapHistoryProvider.get_messages: get_context_for_prompt failed "
84
+ "conversation_id=%s error=%s",
85
+ conv_id,
86
+ exc,
87
+ exc_info=True,
88
+ )
89
+ return []
90
+
91
+ recent = getattr(response, "recent_messages", None) or []
92
+ messages: list[Message] = []
93
+ for rm in recent:
94
+ role = getattr(rm, "role", None)
95
+ content = getattr(rm, "content", None)
96
+ if not role or not content:
97
+ continue
98
+ messages.append(Message(role=role, contents=[content]))
99
+ return messages
100
+
101
+ async def save_messages(
102
+ self,
103
+ session_id: Optional[str],
104
+ messages: Sequence[Message],
105
+ *,
106
+ state: Optional[dict[str, Any]] = None,
107
+ **kwargs: Any,
108
+ ) -> None:
109
+ conv_id = self.conversation_id or session_id
110
+ if not conv_id or not messages:
111
+ return
112
+
113
+ async with wrap_sdk_errors_async(
114
+ "microsoft_agent.save_messages",
115
+ logger,
116
+ user_id=self.user_id,
117
+ conversation_id=conv_id,
118
+ ):
119
+ for message in messages:
120
+ role_raw = getattr(message, "role", None)
121
+ role = role_raw.value if hasattr(role_raw, "value") else str(role_raw)
122
+ if role not in {"user", "assistant", "system"}:
123
+ continue
124
+ text = getattr(message, "text", "") or ""
125
+ if not text.strip():
126
+ continue
127
+ await self.sdk.conversation.record_message(
128
+ conversation_id=conv_id,
129
+ role=role,
130
+ content=text,
131
+ user_id=self.user_id,
132
+ customer_id=self.customer_id,
133
+ )