Add SessionsManager and make ChatObject session-aware#4
Merged
JohnRichard4096 merged 12 commits intomainfrom Feb 10, 2026
Merged
Add SessionsManager and make ChatObject session-aware#4JohnRichard4096 merged 12 commits intomainfrom
JohnRichard4096 merged 12 commits intomainfrom
Conversation
Contributor
Reviewer's GuideIntroduce a SessionsManager-based session isolation system, refactor tools/presets/MCP managers to support per-session instances while keeping singletons, wire sessions into ChatObject and agent tool-calling, add extensive tests and examples for sessions and core types, and enhance docs, CI, and configuration around testing and streaming usage. Sequence diagram for ChatObject creation with SessionsManager session isolationsequenceDiagram
actor User
participant App as ApplicationCode
participant SM as SessionsManager
participant SD as SessionData
participant CO as ChatObject
participant PM as PresetManager
User->>App: Provide session_id and user_input
App->>SM: is_session_registered(session_id)
alt session not registered and auto_create_session
App->>SM: init_session(session_id)
end
App->>SM: get_session_data(session_id, default=None)
alt Session exists
SM-->>App: SessionData
App->>SD: read memory
App->>SD: read config
App->>SD: presets.get_default_preset()
SD-->>App: Memory, AmritaConfig, ModelPreset
App->>CO: __init__(train, user_input, context=None, session_id, config=session.config, preset=session.presets.get_default_preset(), auto_create_session=True)
CO->>SM: get_session_data(session_id)
SM-->>CO: SessionData
CO->>SD: read memory
SD-->>CO: MemoryModel
CO-->>App: ChatObject instance
else Session not found and no auto_create_session
SM-->>App: None
App->>PM: get_default_preset()
PM-->>App: ModelPreset
App->>CO: __init__(train, user_input, context=Memory, session_id, config=get_config(), preset=default_preset)
CO-->>App: ChatObject instance
end
App->>CO: begin() as async context manager
CO-->>User: Streamed responses via get_response_generator()
Class diagram for SessionsManager-based session isolation and managersclassDiagram
direction LR
class SessionData {
+str session_id
+MemoryModel memory
+MultiToolsManager tools
+MultiPresetManager presets
+ClientManager mcp
+AmritaConfig config
}
class SessionsManager {
-dict~str, SessionData~ _session2DataMap
-SessionsManager _instance
-object __marker
+is_session_registered(session_id: str) bool
+get_registered_sessions() dict~str, SessionData~
+init_session(session_id: str) void
+set_session(data: SessionData) void
+get_session_data(session_id: str) SessionData
+get_session_data(session_id: str, default: T) SessionData | T
+new_session() str
+drop_session(session_id: str) void
}
class MultiToolsManager {
-dict~str, ToolData~ _models
-set~str~ _disabled_tools
-bool _inited
+has_tool(name: str) bool
+register_tool(tool: ToolData) void
+remove_tool(name: str) void
+get_tool(name: str) ToolData | None
+tools_meta_dict() dict~str, dict~str, any~~
+disable_tool(name: str) void
+enable_tool(name: str) void
+get_disabled_tools() list~str~
}
class ToolsManager {
-ToolsManager _instance
-bool _initialized
}
class MultiPresetManager {
-ModelPreset | None _default_preset
-dict~str, ModelPreset~ _presets
-bool _inited
+set_default_preset(preset: ModelPreset | str) void
+get_default_preset() ModelPreset
+add_preset(preset: ModelPreset) void
+get_preset(name: str) ModelPreset | None
+list_presets() list~ModelPreset~
+test_single_preset(preset: ModelPreset) PresetReport
+test_presets() PresetReport* (async generator)
}
class PresetManager {
-PresetManager _instance
-bool _initialized
}
class MultiClientManager {
+list~MCPClient~ clients
+dict~str, MCPClient~ script_to_clients
+dict~str, MCPClient~ name_to_clients
+dict~str, str~ tools_remapping
+dict~str, str~ reversed_remappings
+MultiToolsManager tools_manager
+Lock _lock
+bool _is_initialized
+bool _initted
+get_client_by_script(server_script: MCP_SERVER_SCRIPT_TYPE) MCPClient
+get_client_by_name(name: str) MCPClient | None
+register_only(server_script: MCP_SERVER_SCRIPT_TYPE, client: MCPClient) MultiClientManager
+update_tools(client: MCPClient) void
+initialize_scripts_all(scripts: Iterable~MCP_SERVER_SCRIPT_TYPE~) void
+initialize_all(lock: bool) void
+unregister_client(script_name: str | Path, lock: bool) void
-_load_this(client: MCPClient, fail_then_raise: bool) void
-_tools_wrapper(origin_name: str) function
}
class ClientManager {
-ClientManager _instance
-bool _initialized
}
class ChatObject {
-dict~str, str~ train
-Memory data
-str session_id
-USER_INPUT user_input
-Message user_message
-AmritaConfig config
-ModelPreset preset
-datetime timestamp
-datetime time
-datetime last_call
-asyncio.Queue _response_queue
-asyncio.Queue _response_overflow_queue
-bool _queue_done
-UniResponse response
+__init__(train: dict~str, str~, user_input: USER_INPUT, context: Memory | None, session_id: str, callback: RESPONSE_CALLBACK_TYPE | None, config: AmritaConfig | None, preset: ModelPreset | None, auto_create_session: bool, queue_size: int, overflow_queue_size: int)
+begin() async context manager
+get_response_generator() AsyncGenerator~str | ResponseWrapper | UniResponse[str, None], None~
+yield_response(message: MessageWithMetadata) void
+set_queue_done() void
+terminate() void
}
class AgentCorePrehook {
+agent_core(event: PreCompletionEvent, config: AmritaConfig) void
}
class ToolsUsageInAgent {
-str agent_last_step
-SessionsManager session_manager
-MultiToolsManager tools_manager
+run_tools(...)
}
SessionsManager "1" o-- "*" SessionData : manages
SessionData "1" o-- "1" MultiToolsManager : tools
SessionData "1" o-- "1" MultiPresetManager : presets
SessionData "1" o-- "1" ClientManager : mcp
SessionData "1" o-- "1" AmritaConfig : config
SessionData "1" o-- "1" MemoryModel : memory
ToolsManager --|> MultiToolsManager
PresetManager --|> MultiPresetManager
ClientManager --|> MultiClientManager
ChatObject ..> SessionsManager : uses
ChatObject ..> SessionData : reads config, memory, presets
AgentCorePrehook ..> SessionsManager : get_session_data
AgentCorePrehook ..> MultiToolsManager : per-session tools_manager
ToolsUsageInAgent ..> ToolsManager : previous global usage
ToolsUsageInAgent ..> MultiToolsManager : new per-session usage
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - I've found 9 issues, and left some high level feedback:
- In
SendMessageWrap.__init__you referenceuser_queryinif not user_query:but the parameter is namedquery, which will raise aNameError; this should be corrected to use the actual argument name (or a clearer condition) before release. - The new
Multi*manager classes (MultiToolsManager,MultiPresetManager,MultiClientManager) still guard initialization with a class-level_initedflag but store state on instances, so subsequent instances will skip initialization and may lack attributes like_models/clients; consider removing the shared_initedflag or making the classes explicitly singleton to avoid partially initialized instances. - In
SessionsManager.drop_sessionyou indexchat_manager.running_chat_object[session_id]directly, which will throw aKeyErrorif the session has no running chat objects; using.get(session_id, [])or an explicitincheck would make session cleanup more robust.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `SendMessageWrap.__init__` you reference `user_query` in `if not user_query:` but the parameter is named `query`, which will raise a `NameError`; this should be corrected to use the actual argument name (or a clearer condition) before release.
- The new `Multi*` manager classes (`MultiToolsManager`, `MultiPresetManager`, `MultiClientManager`) still guard initialization with a class-level `_inited` flag but store state on instances, so subsequent instances will skip initialization and may lack attributes like `_models`/`clients`; consider removing the shared `_inited` flag or making the classes explicitly singleton to avoid partially initialized instances.
- In `SessionsManager.drop_session` you index `chat_manager.running_chat_object[session_id]` directly, which will throw a `KeyError` if the session has no running chat objects; using `.get(session_id, [])` or an explicit `in` check would make session cleanup more robust.
## Individual Comments
### Comment 1
<location> `src/amrita_core/types.py:226-227` </location>
<code_context>
raise ValueError("Invalid query message, expecting user message!")
self.user_query = query
- self.memory.pop()
+ if not user_query:
+ self.memory.pop()
@classmethod
</code_context>
<issue_to_address>
**issue (bug_risk):** Undefined variable `user_query` and likely inverted condition in `SendMessageWrap.__init__`.
In `SendMessageWrap.__init__`, `if not user_query:` will raise a `NameError` because `user_query` isn’t defined in this scope. You probably meant `if not query:` or to check `self.user_query` after assignment.
This change also alters behavior: previously `self.memory.pop()` always ran; now it only runs when the condition is true. If the intent is just to avoid popping from an empty list, use something like `if self.memory: self.memory.pop()` instead of tying it to the query value.
</issue_to_address>
### Comment 2
<location> `src/amrita_core/chatmanager.py:488-493` </location>
<code_context>
+def load_session(session_id: str):
+ logger.info("Loading session %s......", session_id)
+ sm = SessionsManager()
+ sm.init_session(session_id)
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Potential KeyError when `auto_create_session` is False and no context is provided.
`ChatObject.__init__` first resolves `session` via `get_session_data(session_id, None)`, but later does `self.data = context or sm.get_session_data(session_id).memory` without a default. When `auto_create_session=False` and `context is None`, this second call can raise a `KeyError` because the session was never registered.
If this behavior is intended, consider failing earlier with a clearer error; otherwise, reuse the already-fetched `session` object and handle the missing-session case explicitly, for example:
```python
if context is not None:
self.data = context
elif session is not None:
self.data = session.memory
else:
raise ValueError(f"No context and session '{session_id}' is not initialized")
```
</issue_to_address>
### Comment 3
<location> `tests/test_types.py:21-28` </location>
<code_context>
+class TestModelConfig:
+ """Test ModelConfig class functionality"""
+
+ def test_default_values(self):
+ """Test default values"""
+ config = ModelConfig()
+ assert config.top_k == 50
+ assert config.top_p == 0.95
+ assert config.temperature == 0.7
+ assert config.stream is False
+ assert config.thought_chain_model is False
+ assert config.multimodal is False
+
</code_context>
<issue_to_address>
**issue (testing):** Update ModelConfig tests to match the new schema and defaults
This test still asserts the old defaults (`top_p=0.95`, `temperature=0.7`) and expects `thought_chain_model`, which no longer exists, so it will now fail. Please update the expectations to the new defaults and drop checks for removed fields (or reintroduce the field if it’s still required, keeping code and tests consistent).
</issue_to_address>
### Comment 4
<location> `tests/test_integration.py:56-62` </location>
<code_context>
+ model="gpt-3.5-turbo", name="test-default", api_key="fake-key"
+ )
+
+ chat_obj = ChatObject(
+ train=train,
+ user_input=user_input,
+ context=context,
+ session_id=session_id,
+ preset=default_preset, # Pass a preset to avoid the error
+ )
+
</code_context>
<issue_to_address>
**issue (testing):** Avoid real network/LLM calls in integration tests that use ChatObject.begin()
Here and in `test_full_workflow`, `test_concurrent_sessions_and_objects`, and `TestChatObject.test_chat_object_run_flow`, `ChatObject.begin()` is invoked with presets using fake API keys. Without mocking the LLM layer, these tests will try real network calls and either fail or be flaky. Please mock the adapter/LLM layer (e.g., monkeypatch `call_completion` or the adapter’s `call_api`/`call_tools`), or use a dedicated stub adapter in tests so `begin()` / `_run` complete without external dependencies.
</issue_to_address>
### Comment 5
<location> `tests/test_chatmanager.py:40-45` </location>
<code_context>
+ model="gpt-3.5-turbo", name="test-default", api_key="fake-key"
+ )
+
+ chat_obj = ChatObject(
+ train=train,
+ user_input=user_input,
+ context=context,
+ session_id=session_id,
+ preset=default_preset, # Pass a preset to avoid the error
+ )
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests for ChatObject session-aware behavior (auto_create_session and session-derived config/preset)
These changes introduce session-derived `config`/`preset` behavior and `auto_create_session`, but the tests still always pass an explicit `context` (and often an explicit `preset`), so the new paths aren’t covered. Please add tests that: (1) build a `ChatObject` with `context=None` and no `config`/`preset` and assert it uses the session’s `memory`, `config`, and default preset; and (2) set `auto_create_session=True` with an unregistered `session_id` and assert the session is created and wired correctly.
Suggested implementation:
```python
from amrita_core.chatmanager import ChatObject, MemoryLimiter, chat_manager
from amrita_core.config import AmritaConfig
from amrita_core.sessions import SessionsManager
from amrita_core.types import (
CONTENT_LIST_TYPE_ITEM,
MemoryModel,
Message,
ModelPreset,
)
def test_chatobject_uses_session_memory_config_and_default_preset():
"""
Building a ChatObject with context=None and no config/preset should cause it
to use the session's memory, config, and default preset.
"""
session_id = "session-defaults-test"
# Prepare session-backed memory/config/preset
session_memory = MemoryModel()
session_config = AmritaConfig()
default_preset = ModelPreset(
model="gpt-3.5-turbo",
name="session-default",
api_key="fake-key",
)
# Ensure the session is registered with the global chat_manager / sessions
# API; this follows the same conventions as the rest of the test suite.
sessions_manager = SessionsManager()
# Create/register the session; the concrete API may differ slightly,
# but the intent is to have a session with the above memory/config/preset.
session = sessions_manager.create_session(
session_id=session_id,
memory=session_memory,
config=session_config,
presets=[default_preset],
)
# Wire this sessions manager into the chat_manager so ChatObject can resolve it.
chat_manager.sessions = sessions_manager
# No explicit context/config/preset passed in – ChatObject should derive
# these from the session.
train = {"role": "system", "content": "system message"}
user_input = "hello from user"
chat_obj = ChatObject(
train=train,
user_input=user_input,
context=None,
session_id=session_id,
config=None,
preset=None,
)
# ChatObject should be using the session's memory/config/preset
assert chat_obj.context is session_memory or getattr(chat_obj, "memory", None) is session_memory
assert getattr(chat_obj, "config", None) is session_config
assert getattr(chat_obj, "preset", None) == default_preset
def test_chatobject_auto_create_session_for_unknown_session_id():
"""
When auto_create_session=True and the session_id is not registered, the
ChatObject should trigger session creation and wire itself to that session.
"""
session_id = "auto-create-session-test"
# Start with a fresh SessionsManager that does not know about this session_id.
sessions_manager = SessionsManager()
chat_manager.sessions = sessions_manager
train = {"role": "system", "content": "system message"}
user_input = "hello auto create"
# No context/config/preset, unknown session_id, but auto_create_session=True.
chat_obj = ChatObject(
train=train,
user_input=user_input,
context=None,
session_id=session_id,
config=None,
preset=None,
auto_create_session=True,
)
# The session should have been created
session = sessions_manager.get_session(session_id)
assert session is not None
# ChatObject should be wired to the session's memory (and config/preset if present)
assert isinstance(chat_obj.context, MemoryModel) or isinstance(
getattr(chat_obj, "memory", None), MemoryModel
)
# If the session exposes config/preset defaults, verify ChatObject picked them up.
session_config = getattr(session, "config", None)
if session_config is not None:
assert getattr(chat_obj, "config", None) is session_config
session_default_preset = getattr(session, "default_preset", None)
if session_default_preset is None:
# Some implementations may expose presets as a list; fall back to the first one.
presets = getattr(session, "presets", None)
if presets:
session_default_preset = presets[0]
if session_default_preset is not None:
assert getattr(chat_obj, "preset", None) == session_default_preset
```
The above tests assume the following APIs based on typical patterns in this codebase:
1. `SessionsManager.create_session(session_id=..., memory=..., config=..., presets=[...])` exists and returns a session object.
2. `chat_manager.sessions` is assignable and is used by `ChatObject` to resolve sessions.
3. `SessionsManager.get_session(session_id)` returns a session or `None`.
4. `ChatObject` exposes `context` (or `memory`), `config`, and `preset` attributes after initialization.
5. A session object exposes either:
- `config` and `default_preset`, or
- `config` and `presets` (list) where the first element is the default preset.
If your actual APIs differ, adjust:
- The `create_session` call to match the real signature.
- The way sessions are attached to `chat_manager` (e.g., `chat_manager.sessions_manager` instead of `chat_manager.sessions`).
- The attribute accesses (`memory` vs `context`, `default_preset` vs `presets[0]`, etc.) to match your concrete `ChatObject` and session implementations.
</issue_to_address>
### Comment 6
<location> `tests/test_sessions.py:65-74` </location>
<code_context>
+ updated_config = manager.get_session_data(session_id).config
+ assert updated_config.llm.max_retries == 5
+
+ def test_safe_getters(self):
+ """Test safe getters"""
+ manager = SessionsManager()
+
+ # Try to get non-existent session
+ non_existent_session = "non-existent-session-id"
+ try:
+ manager.get_session_data(non_existent_session)
+ pytest.fail("Expected KeyError was not raised")
+ except KeyError:
+ pass # Expected behavior
+
+ # Create session and verify safe getters
+ session_id = manager.new_session()
+ session_data = manager.get_session_data(session_id)
+ assert session_data.config is not None
+ assert session_data.tools is not None
+ assert session_data.memory is not None
+
+ def test_session_drop(self):
</code_context>
<issue_to_address>
**suggestion (testing):** Extend SessionsManager tests to cover default-return path and set_session/init_session behavior
These tests cover the singleton pattern, `new_session`, and the error-on-missing-session behavior, but they skip the `default` parameter of `get_session_data`, `init_session` on an existing `session_id`, and `set_session`. To better validate the SessionsManager API, please add tests that: (1) call `get_session_data("missing", default_value)` and assert the default is returned without raising; (2) call `init_session` twice with the same `session_id` and ensure existing data is preserved; and (3) exercise `set_session` to confirm the provided `SessionData` is stored and retrievable.
</issue_to_address>
### Comment 7
<location> `tests/test_tools.py:23-29` </location>
<code_context>
+ assert manager._models == {}
+ assert manager._disabled_tools == set()
+
+ def test_register_and_get_tools(self):
+ """Test register and get tools functionality"""
+ manager = MultiToolsManager()
+
+ # Initially there should be no tools
+ all_tools = manager.get_tools()
+ assert len(all_tools) == 0
+
+ # Check if tools exist after registration would go here
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for MultiToolsManager register/remove/disable behavior instead of only the empty case
This only checks the initial empty state and misses the key behaviors. Please add tests that register a dummy `ToolData` tool and assert: (1) `has_tool`/`get_tool` reflect it after registration; (2) `disable_tool` hides it from lookups but keeps the underlying model; and (3) `remove_tool` deletes it entirely from `get_tools` and related methods.
Suggested implementation:
```python
def test_register_and_get_tools(self):
"""Test register, disable, remove, and get tools functionality"""
manager = MultiToolsManager()
# Initially there should be no tools
all_tools = manager.get_tools()
assert len(all_tools) == 0
# Register a dummy tool
tool_name = "dummy_tool"
dummy_tool = object()
manager.register_tool(tool_name, dummy_tool)
# (1) After registration: has_tool / get_tool reflect it
assert manager.has_tool(tool_name) is True
assert manager.get_tool(tool_name) is dummy_tool
# get_tools should include the registered tool and it should not be disabled
all_tools = manager.get_tools()
assert len(all_tools) == 1
# Depending on implementation, get_tools might return a dict or iterable of names/models.
# These assertions are intentionally loose: they only require that the tool is visible.
assert tool_name in str(all_tools)
assert tool_name not in manager._disabled_tools
assert manager._models[tool_name] is dummy_tool
# (2) disable_tool hides it from lookups but keeps the underlying model
manager.disable_tool(tool_name)
# Tool should no longer be reported as available
assert manager.has_tool(tool_name) is False
# get_tool should respect disablement (assuming default=None behavior)
assert manager.get_tool(tool_name, default=None) is None
# Underlying model should still be present, and tool should be marked as disabled
assert tool_name in manager._models
assert manager._models[tool_name] is dummy_tool
assert tool_name in manager._disabled_tools
# get_tools should not include the disabled tool
all_tools_after_disable = manager.get_tools()
assert tool_name not in str(all_tools_after_disable)
# (3) remove_tool deletes it entirely
manager.remove_tool(tool_name)
# Tool should be gone from internal registries
assert tool_name not in manager._models
assert tool_name not in manager._disabled_tools
# Public API should also reflect removal
assert manager.has_tool(tool_name) is False
assert manager.get_tool(tool_name, default=None) is None
all_tools_after_remove = manager.get_tools()
assert tool_name not in str(all_tools_after_remove)
```
The above assumes the following public API on `MultiToolsManager`:
- `register_tool(name: str, model: Any) -> None`
- `disable_tool(name: str) -> None`
- `remove_tool(name: str) -> None`
- `has_tool(name: str) -> bool`
- `get_tool(name: str, default: Any | None = None) -> Any | None`
- `get_tools() -> Any` (iterable or mapping that conceptually “contains” the registered, non-disabled tools)
If your signatures differ (e.g. `register_tool` takes a `ToolData` object instead of `name, model`, or `get_tools` returns a dict of `{name: model}`), adjust:
- The `register_tool(...)` call to use the correct argument shape.
- The loose `assert tool_name in str(all_tools)` / `str(...)` checks to more precise membership checks, such as:
- `assert tool_name in all_tools` if `all_tools` is a dict of `{name: model}`; or
- `assert any(t.name == tool_name for t in all_tools)` if `all_tools` is a list of `ToolData` objects.
Also ensure that `get_tool` supports the `default` parameter; if it does not, replace `get_tool(tool_name, default=None)` with whatever “no such tool” behavior your implementation uses (e.g. catching a `KeyError` or custom exception).
</issue_to_address>
### Comment 8
<location> `docs/zh/guide/builtins.md:57` </location>
<code_context>
-AmritaCore 包含一个智能Agent系统,能够自主地使用工具来完成任务。
+AmritaCore 包含一个智能体系统,能够自主地使用工具来完成任务。
### 9.3.1 Agent工作流程
</code_context>
<issue_to_address>
**suggestion (typo):** Consider updating the section title to use “智能体” to match the surrounding text.
The body text now uses “智能体系统”, but the heading still says “Agent工作流程”. Please rename the heading (e.g., to “智能体工作流程”) to keep terminology consistent within this chapter.
Suggested implementation:
```
## 9.3 内置智能体系统
```
Also update the subsection heading elsewhere in the same file:
- Change:
`### 9.3.1 Agent工作流程`
- To:
`### 9.3.1 智能体工作流程`
This will ensure both the section and subsection titles consistently use “智能体” to match the body text (“智能体系统”).
</issue_to_address>
### Comment 9
<location> `docs/guide/function-implementation.md:120` </location>
<code_context>
```python
-# Execute the conversation
+# Execute the conversation (this is recommended when you are using callback function)
await chat.begin()
</code_context>
<issue_to_address>
**nitpick (typo):** Fix minor grammar in the inline comment (“a callback function” or “callback functions”).
The phrase “when you are using callback function” is ungrammatical. Please change it to “when you are using a callback function” or “when you are using callback functions.”
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Deploying amritacore with
|
| Latest commit: |
671fb51
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8ee54a5a.amritacore.pages.dev |
| Branch Preview URL: | https://feat-sessions-manage.amritacore.pages.dev |
Member
Author
|
@sourcery-ai title |
Member
Author
|
@sourcery-ai summary |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by Sourcery
Introduce session-aware architecture with a SessionsManager and per-session tool/preset/config management, update ChatObject and related components to use session data, and add comprehensive tests and documentation for the new session isolation model.
New Features:
Enhancements:
Build:
CI:
Documentation:
Tests: