mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 20:50:58 +00:00
feat(tenancy): implement workspace isolation
This commit is contained in:
@@ -1,12 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import dataclasses
|
||||
|
||||
from ...core import app
|
||||
from langbot_plugin.api.entities.builtin.provider import message as provider_message, prompt as provider_prompt
|
||||
import langbot_plugin.api.entities.builtin.provider.session as provider_session
|
||||
import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
|
||||
|
||||
from ...api.http.context import ExecutionContext
|
||||
from ...core import app
|
||||
from ...pipeline.pool import (
|
||||
ExecutionContextMismatchError,
|
||||
ExecutionContextRequiredError,
|
||||
bind_execution_context,
|
||||
get_query_execution_context,
|
||||
)
|
||||
|
||||
SessionKey = tuple[
|
||||
str,
|
||||
str,
|
||||
int,
|
||||
str,
|
||||
str,
|
||||
int | str,
|
||||
]
|
||||
|
||||
|
||||
def _query_session_key(query: pipeline_query.Query) -> tuple[SessionKey, ExecutionContext]:
|
||||
execution_context = get_query_execution_context(query)
|
||||
bot_uuid = getattr(query, 'bot_uuid', None)
|
||||
if not isinstance(bot_uuid, str) or not bot_uuid.strip():
|
||||
raise ExecutionContextRequiredError('Query.bot_uuid is required for session lookup')
|
||||
|
||||
execution_context = bind_execution_context(execution_context, bot_uuid=bot_uuid)
|
||||
key: SessionKey = (
|
||||
execution_context.instance_uuid,
|
||||
execution_context.workspace_uuid,
|
||||
execution_context.placement_generation,
|
||||
bot_uuid,
|
||||
query.launcher_type.value,
|
||||
query.launcher_id,
|
||||
)
|
||||
return key, execution_context
|
||||
|
||||
|
||||
class SessionManager:
|
||||
"""会话管理器"""
|
||||
@@ -24,17 +60,39 @@ class SessionManager:
|
||||
|
||||
async def get_session(self, query: pipeline_query.Query) -> provider_session.Session:
|
||||
"""获取会话"""
|
||||
session_key, execution_context = _query_session_key(query)
|
||||
for session in self.session_list:
|
||||
if query.launcher_type == session.launcher_type and query.launcher_id == session.launcher_id:
|
||||
if getattr(session, '_langbot_session_key', None) == session_key:
|
||||
return session
|
||||
|
||||
session_concurrency = self.ap.instance_config.data['concurrency']['session']
|
||||
|
||||
session = provider_session.Session(
|
||||
instance_uuid=execution_context.instance_uuid,
|
||||
workspace_uuid=execution_context.workspace_uuid,
|
||||
placement_generation=execution_context.placement_generation,
|
||||
bot_uuid=query.bot_uuid,
|
||||
launcher_type=query.launcher_type,
|
||||
launcher_id=query.launcher_id,
|
||||
sender_id=query.sender_id,
|
||||
)
|
||||
session_context = dataclasses.replace(
|
||||
execution_context,
|
||||
pipeline_uuid=None,
|
||||
query_uuid=None,
|
||||
)
|
||||
# langbot-plugin 0.4.13 ignores Workspace fields. Preserve them until
|
||||
# the Workspace-aware SDK becomes the minimum supported version.
|
||||
object.__setattr__(session, 'instance_uuid', session_context.instance_uuid)
|
||||
object.__setattr__(session, 'workspace_uuid', session_context.workspace_uuid)
|
||||
object.__setattr__(
|
||||
session,
|
||||
'placement_generation',
|
||||
session_context.placement_generation,
|
||||
)
|
||||
object.__setattr__(session, 'bot_uuid', query.bot_uuid)
|
||||
object.__setattr__(session, '_execution_context', session_context)
|
||||
object.__setattr__(session, '_langbot_session_key', session_key)
|
||||
session._semaphore = asyncio.Semaphore(session_concurrency)
|
||||
self.session_list.append(session)
|
||||
return session
|
||||
@@ -49,6 +107,17 @@ class SessionManager:
|
||||
) -> provider_session.Conversation:
|
||||
"""获取对话或创建对话"""
|
||||
|
||||
session_key, execution_context = _query_session_key(query)
|
||||
if getattr(session, '_langbot_session_key', None) != session_key:
|
||||
raise ExecutionContextMismatchError('Session does not belong to the Query execution scope')
|
||||
execution_context = bind_execution_context(
|
||||
execution_context,
|
||||
bot_uuid=bot_uuid,
|
||||
pipeline_uuid=pipeline_uuid,
|
||||
)
|
||||
if execution_context.bot_uuid != getattr(session, 'bot_uuid', None):
|
||||
raise ExecutionContextMismatchError('Session bot_uuid does not match the Query execution scope')
|
||||
|
||||
if not session.conversations:
|
||||
session.conversations = []
|
||||
|
||||
@@ -63,7 +132,11 @@ class SessionManager:
|
||||
messages=prompt_messages,
|
||||
)
|
||||
|
||||
if session.using_conversation is None or session.using_conversation.pipeline_uuid != pipeline_uuid:
|
||||
if (
|
||||
session.using_conversation is None
|
||||
or session.using_conversation.pipeline_uuid != pipeline_uuid
|
||||
or session.using_conversation.bot_uuid != bot_uuid
|
||||
):
|
||||
conversation = provider_session.Conversation(
|
||||
prompt=prompt,
|
||||
messages=[],
|
||||
|
||||
Reference in New Issue
Block a user