diff --git a/src/langbot/pkg/api/http/service/bot.py b/src/langbot/pkg/api/http/service/bot.py index 1074c9d92..3e34c1af7 100644 --- a/src/langbot/pkg/api/http/service/bot.py +++ b/src/langbot/pkg/api/http/service/bot.py @@ -776,7 +776,8 @@ class BotService: raise WorkspaceNotFoundError('Bot not found') runtime_bot = await self.ap.platform_mgr.get_bot_by_uuid(context, bot_uuid) if runtime_bot is None: - raise Exception('Bot not found') + # Saved but disabled/unconfigured bots have no runtime logs yet. + return [], 0 logs, total_count = await runtime_bot.logger.get_logs(from_index, max_count) diff --git a/src/langbot/pkg/entity/persistence/metadata.py b/src/langbot/pkg/entity/persistence/metadata.py index ac22cd2e9..b60fd9dd4 100644 --- a/src/langbot/pkg/entity/persistence/metadata.py +++ b/src/langbot/pkg/entity/persistence/metadata.py @@ -9,7 +9,7 @@ class Metadata(Base): __tablename__ = 'metadata' key = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True) - value = sqlalchemy.Column(sqlalchemy.String(255)) + value = sqlalchemy.Column(sqlalchemy.Text()) class WorkspaceMetadata(Base): @@ -23,4 +23,4 @@ class WorkspaceMetadata(Base): primary_key=True, ) key = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True) - value = sqlalchemy.Column(sqlalchemy.String(255)) + value = sqlalchemy.Column(sqlalchemy.Text()) diff --git a/src/langbot/pkg/persistence/alembic/versions/0037_metadata_text.py b/src/langbot/pkg/persistence/alembic/versions/0037_metadata_text.py new file mode 100644 index 000000000..dd6922cb4 --- /dev/null +++ b/src/langbot/pkg/persistence/alembic/versions/0037_metadata_text.py @@ -0,0 +1,22 @@ +"""Allow structured wizard progress in metadata values.""" + +from alembic import op +import sqlalchemy as sa + +revision = '0037_metadata_text' +down_revision = '0036_execution_host_leases' +branch_labels = None +depends_on = None + + +def upgrade(): + inspector = sa.inspect(op.get_bind()) + for table in ('metadata', 'workspace_metadata'): + if inspector.has_table(table): + with op.batch_alter_table(table) as batch: + batch.alter_column('value', existing_type=sa.String(255), type_=sa.Text(), existing_nullable=True) + + +def downgrade(): + # Keep the widened type: narrowing would destroy valid saved progress. + pass diff --git a/src/langbot/templates/embed/widget.js b/src/langbot/templates/embed/widget.js index 72e3f5355..63d00f737 100644 --- a/src/langbot/templates/embed/widget.js +++ b/src/langbot/templates/embed/widget.js @@ -174,6 +174,17 @@ bubbleIcon: "__LANGBOT_BUBBLE_ICON__", }; + // Wizard previews must use the browser-visible backend URL. Behind a TLS + // proxy the server's request URL can be HTTP even when widget.js uses HTTPS. + // External embeds retain their configured public webhook prefix. + if (scriptEl && scriptEl.src) { + var scriptUrl = new URL(scriptEl.src, document.baseURI); + if (scriptUrl.searchParams.get("preview") === "wizard") { + CONFIG.baseUrl = scriptUrl.origin; + CONFIG.logoUrl = CONFIG.baseUrl + "/api/v1/embed/logo"; + } + } + // ========== Styles ========== var STYLES = '\ diff --git a/tests/unit_tests/api/service/test_wizard_regressions.py b/tests/unit_tests/api/service/test_wizard_regressions.py new file mode 100644 index 000000000..e79ac43ef --- /dev/null +++ b/tests/unit_tests/api/service/test_wizard_regressions.py @@ -0,0 +1,20 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import pytest +import sqlalchemy + +from langbot.pkg.entity.persistence.metadata import Metadata, WorkspaceMetadata +from langbot.pkg.api.http.service.bot import BotService + + +def test_metadata_supports_long_wizard_json(): + for model in (Metadata, WorkspaceMetadata): + assert isinstance(model.__table__.c.value.type, sqlalchemy.Text) + + +@pytest.mark.asyncio +async def test_saved_bot_without_runtime_has_empty_logs(): + service = BotService(SimpleNamespace(platform_mgr=SimpleNamespace(get_bot_by_uuid=AsyncMock(return_value=None)))) + service.get_bot = AsyncMock(return_value={'uuid': 'bot'}) + assert await service.list_event_logs('workspace', 'bot', 0, 20) == ([], 0)