mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-21 16:05:19 +08:00
feat: simplify resource setup and detail guides
This commit is contained in:
@@ -71,23 +71,26 @@ class KnowledgeService:
|
||||
|
||||
creation_settings = restore_secret_placeholders(kb_data.get('creation_settings', {}))
|
||||
retrieval_settings = kb_data.get('retrieval_settings', {})
|
||||
defer_initialization = kb_data.get('defer_initialization') is True
|
||||
|
||||
# Validate required fields based on plugin's creation_schema and retrieval_schema
|
||||
await self._validate_schema_required_fields(
|
||||
context,
|
||||
knowledge_engine_plugin_id,
|
||||
creation_settings,
|
||||
retrieval_settings,
|
||||
)
|
||||
if not defer_initialization:
|
||||
await self._validate_schema_required_fields(
|
||||
context,
|
||||
knowledge_engine_plugin_id,
|
||||
creation_settings,
|
||||
retrieval_settings,
|
||||
)
|
||||
|
||||
kb = await self.ap.rag_mgr.create_knowledge_base(
|
||||
context,
|
||||
name=kb_data.get('name', 'Untitled'),
|
||||
knowledge_engine_plugin_id=knowledge_engine_plugin_id,
|
||||
creation_settings=creation_settings,
|
||||
retrieval_settings=retrieval_settings,
|
||||
description=kb_data.get('description', ''),
|
||||
)
|
||||
create_kwargs = {
|
||||
'name': kb_data.get('name', 'Untitled'),
|
||||
'knowledge_engine_plugin_id': knowledge_engine_plugin_id,
|
||||
'creation_settings': creation_settings,
|
||||
'retrieval_settings': retrieval_settings,
|
||||
'description': kb_data.get('description', ''),
|
||||
}
|
||||
if defer_initialization:
|
||||
create_kwargs['initialize'] = False
|
||||
kb = await self.ap.rag_mgr.create_knowledge_base(context, **create_kwargs)
|
||||
return kb.uuid
|
||||
|
||||
async def _validate_schema_required_fields(
|
||||
@@ -205,10 +208,32 @@ class KnowledgeService:
|
||||
) -> None:
|
||||
"""更新知识库"""
|
||||
workspace_uuid = require_workspace_uuid(context)
|
||||
if await self.get_knowledge_base(context, kb_uuid) is None:
|
||||
current = await self.get_knowledge_base(context, kb_uuid, include_secret=True)
|
||||
if current is None:
|
||||
raise WorkspaceNotFoundError('Knowledge base not found')
|
||||
# Filter to only mutable fields
|
||||
filtered_data = {k: v for k, v in kb_data.items() if k in persistence_rag.KnowledgeBase.MUTABLE_FIELDS}
|
||||
|
||||
should_initialize = current.get('initialized', True) is False and kb_data.get('initialize_engine') is True
|
||||
if should_initialize:
|
||||
plugin_id = current.get('knowledge_engine_plugin_id')
|
||||
if not plugin_id:
|
||||
raise ValueError('knowledge_engine_plugin_id is required')
|
||||
creation_settings = restore_secret_placeholders(kb_data.get('creation_settings', {}))
|
||||
retrieval_settings = kb_data.get('retrieval_settings', {})
|
||||
await self._validate_schema_required_fields(
|
||||
context,
|
||||
plugin_id,
|
||||
creation_settings,
|
||||
retrieval_settings,
|
||||
)
|
||||
filtered_data = {
|
||||
'name': kb_data.get('name', current.get('name', 'Untitled')),
|
||||
'description': kb_data.get('description', current.get('description', '')),
|
||||
'creation_settings': creation_settings,
|
||||
'retrieval_settings': retrieval_settings,
|
||||
'initialized': True,
|
||||
}
|
||||
else:
|
||||
filtered_data = {k: v for k, v in kb_data.items() if k in persistence_rag.KnowledgeBase.MUTABLE_FIELDS}
|
||||
|
||||
if not filtered_data:
|
||||
return
|
||||
@@ -224,8 +249,28 @@ class KnowledgeService:
|
||||
kb = await self.get_knowledge_base(context, kb_uuid, include_secret=True)
|
||||
if kb is None:
|
||||
raise WorkspaceNotFoundError('Knowledge base not found')
|
||||
if kb.get('initialized', True) is False:
|
||||
return
|
||||
|
||||
await self.ap.rag_mgr.load_knowledge_base(context, kb)
|
||||
runtime_kb = await self.ap.rag_mgr.load_knowledge_base(context, kb)
|
||||
if should_initialize:
|
||||
try:
|
||||
await runtime_kb._on_kb_create(self._execution_context(context))
|
||||
except Exception:
|
||||
await self.ap.rag_mgr.remove_knowledge_base_from_runtime(context, kb_uuid)
|
||||
await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.update(persistence_rag.KnowledgeBase)
|
||||
.values(
|
||||
name=current.get('name', 'Untitled'),
|
||||
description=current.get('description', ''),
|
||||
creation_settings=current.get('creation_settings', {}),
|
||||
retrieval_settings=current.get('retrieval_settings', {}),
|
||||
initialized=False,
|
||||
)
|
||||
.where(persistence_rag.KnowledgeBase.workspace_uuid == workspace_uuid)
|
||||
.where(persistence_rag.KnowledgeBase.uuid == kb_uuid)
|
||||
)
|
||||
raise
|
||||
|
||||
async def _check_doc_capability(self, context: TenantContext, kb_uuid: str, operation: str) -> None:
|
||||
"""Check if the KB's Knowledge Engine supports document operations.
|
||||
|
||||
@@ -29,6 +29,12 @@ class KnowledgeBase(Base):
|
||||
)
|
||||
creation_settings = sqlalchemy.Column(sqlalchemy.JSON, nullable=True, default=None)
|
||||
retrieval_settings = sqlalchemy.Column(sqlalchemy.JSON, nullable=True, default=None)
|
||||
initialized = sqlalchemy.Column(
|
||||
sqlalchemy.Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=sqlalchemy.true(),
|
||||
)
|
||||
# Server-selected pgvector dimension. ``None`` means no embedding has been
|
||||
# written yet; the first pgvector upsert binds it atomically.
|
||||
embedding_dimension = sqlalchemy.Column(sqlalchemy.Integer, nullable=True)
|
||||
@@ -44,6 +50,7 @@ class KnowledgeBase(Base):
|
||||
'workspace_uuid',
|
||||
'legacy_vector_collection',
|
||||
'embedding_dimension',
|
||||
'initialized',
|
||||
'emoji',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Add deferred initialization state for knowledge bases."""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '0026_knowledge_base_drafts'
|
||||
down_revision = '0025_bot_plugin_processors'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
if not inspector.has_table('knowledge_bases'):
|
||||
return
|
||||
if 'initialized' not in {column['name'] for column in inspector.get_columns('knowledge_bases')}:
|
||||
op.add_column(
|
||||
'knowledge_bases',
|
||||
sa.Column('initialized', sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
if not inspector.has_table('knowledge_bases'):
|
||||
return
|
||||
if 'initialized' in {column['name'] for column in inspector.get_columns('knowledge_bases')}:
|
||||
op.drop_column('knowledge_bases', 'initialized')
|
||||
@@ -802,6 +802,7 @@ class RAGManager:
|
||||
creation_settings: dict,
|
||||
retrieval_settings: dict | None = None,
|
||||
description: str = '',
|
||||
initialize: bool = True,
|
||||
) -> persistence_rag.KnowledgeBase:
|
||||
"""Create a new knowledge base using a RAG plugin."""
|
||||
execution_context = await self._to_execution_context(context)
|
||||
@@ -831,6 +832,7 @@ class RAGManager:
|
||||
'collection_id': collection_id,
|
||||
'creation_settings': creation_settings,
|
||||
'retrieval_settings': retrieval_settings or {},
|
||||
'initialized': initialize,
|
||||
}
|
||||
|
||||
# Create Entity
|
||||
@@ -839,20 +841,21 @@ class RAGManager:
|
||||
# Persist
|
||||
await self.ap.persistence_mgr.execute_async(sqlalchemy.insert(persistence_rag.KnowledgeBase).values(kb_data))
|
||||
|
||||
# Load into Runtime
|
||||
runtime_kb = await self.load_knowledge_base(execution_context, kb)
|
||||
if initialize:
|
||||
# Drafts stay out of the runtime until their engine settings are saved.
|
||||
runtime_kb = await self.load_knowledge_base(execution_context, kb)
|
||||
|
||||
# Notify Plugin — rollback DB record and runtime entry on failure
|
||||
try:
|
||||
await runtime_kb._on_kb_create(execution_context)
|
||||
except Exception:
|
||||
self._pop_runtime(execution_context, kb_uuid)
|
||||
await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.delete(persistence_rag.KnowledgeBase)
|
||||
.where(persistence_rag.KnowledgeBase.workspace_uuid == execution_context.workspace_uuid)
|
||||
.where(persistence_rag.KnowledgeBase.uuid == kb_uuid)
|
||||
)
|
||||
raise
|
||||
# Roll back the record and runtime entry if plugin initialization fails.
|
||||
try:
|
||||
await runtime_kb._on_kb_create(execution_context)
|
||||
except Exception:
|
||||
self._pop_runtime(execution_context, kb_uuid)
|
||||
await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.delete(persistence_rag.KnowledgeBase)
|
||||
.where(persistence_rag.KnowledgeBase.workspace_uuid == execution_context.workspace_uuid)
|
||||
.where(persistence_rag.KnowledgeBase.uuid == kb_uuid)
|
||||
)
|
||||
raise
|
||||
|
||||
self.ap.logger.info(f'Created new Knowledge Base {name} ({kb_uuid}) using plugin {knowledge_engine_plugin_id}')
|
||||
return kb
|
||||
@@ -878,6 +881,8 @@ class RAGManager:
|
||||
.order_by(persistence_rag.KnowledgeBase.uuid)
|
||||
)
|
||||
for knowledge_base in result.all():
|
||||
if knowledge_base.initialized is False:
|
||||
continue
|
||||
try:
|
||||
await self.load_knowledge_base(
|
||||
ExecutionContext(
|
||||
@@ -899,6 +904,8 @@ class RAGManager:
|
||||
knowledge_bases = result.all()
|
||||
|
||||
for knowledge_base in knowledge_bases:
|
||||
if knowledge_base.initialized is False:
|
||||
continue
|
||||
try:
|
||||
binding = await self.ap.workspace_service.get_execution_binding(knowledge_base.workspace_uuid)
|
||||
execution_context = ExecutionContext(
|
||||
|
||||
Reference in New Issue
Block a user