mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-26 23:44:19 +00:00
Merge remote-tracking branch 'origin/feat/agent-runner-plugin' into dev_4_11
# Conflicts: # src/langbot/pkg/pipeline/preproc/preproc.py
This commit is contained in:
@@ -13,6 +13,28 @@ from sqlalchemy.engine import Connection
|
||||
|
||||
from langbot.pkg.entity.persistence.base import Base
|
||||
|
||||
# Import all ORM models so they are registered with Base.metadata
|
||||
# This is required for autogenerate to detect model changes
|
||||
from langbot.pkg.entity.persistence import (
|
||||
agent_run, # noqa: F401
|
||||
agent_runner_state, # noqa: F401
|
||||
apikey, # noqa: F401
|
||||
bot, # noqa: F401
|
||||
bstorage, # noqa: F401
|
||||
event_log, # noqa: F401
|
||||
mcp, # noqa: F401
|
||||
metadata, # noqa: F401
|
||||
model, # noqa: F401
|
||||
monitoring, # noqa: F401
|
||||
pipeline, # noqa: F401
|
||||
plugin, # noqa: F401
|
||||
rag, # noqa: F401
|
||||
transcript, # noqa: F401
|
||||
user, # noqa: F401
|
||||
vector, # noqa: F401
|
||||
webhook, # noqa: F401
|
||||
)
|
||||
|
||||
target_metadata = Base.metadata
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
"""Normalize AgentRunner config containers
|
||||
|
||||
Revision ID: 0005_migrate_runner_config
|
||||
Revises: 0005_add_llm_context_length
|
||||
Create Date: 2026-05-10
|
||||
"""
|
||||
|
||||
import json
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
from langbot.pkg.agent.runner.config_migration import ConfigMigration
|
||||
|
||||
revision = '0005_migrate_runner_config'
|
||||
down_revision = '0005_add_llm_context_length'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
def migrate_pipeline_config(config: dict) -> dict:
|
||||
"""Migrate persisted pipeline config to the AgentRunner plugin shape."""
|
||||
return ConfigMigration.migrate_pipeline_config(config)
|
||||
|
||||
|
||||
def _load_config(config_value):
|
||||
if isinstance(config_value, dict):
|
||||
return config_value
|
||||
if isinstance(config_value, str):
|
||||
return json.loads(config_value)
|
||||
return None
|
||||
|
||||
|
||||
def _update_config(conn, table_name: str, pipeline_uuid: str, migrated_config: dict) -> None:
|
||||
"""Write JSON config using a dialect-compatible bind."""
|
||||
config_json = json.dumps(migrated_config)
|
||||
if conn.dialect.name == 'postgresql':
|
||||
conn.execute(
|
||||
sa.text(
|
||||
f'UPDATE {table_name} '
|
||||
'SET config = CAST(:config AS JSON) '
|
||||
'WHERE uuid = :uuid'
|
||||
),
|
||||
{'config': config_json, 'uuid': pipeline_uuid},
|
||||
)
|
||||
return
|
||||
|
||||
conn.execute(
|
||||
sa.text(f'UPDATE {table_name} SET config = :config WHERE uuid = :uuid'),
|
||||
{'config': config_json, 'uuid': pipeline_uuid},
|
||||
)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Normalize existing pipeline config containers."""
|
||||
conn = op.get_bind()
|
||||
inspector = sa.inspect(conn)
|
||||
|
||||
table_name = 'legacy_pipelines'
|
||||
|
||||
# Check if pipeline table exists (may not exist in fresh install)
|
||||
if table_name not in inspector.get_table_names():
|
||||
return
|
||||
|
||||
# Get all pipelines
|
||||
result = conn.execute(sa.text(f'SELECT uuid, config FROM {table_name}'))
|
||||
pipelines = result.fetchall()
|
||||
|
||||
for pipeline_uuid, config_json in pipelines:
|
||||
if not config_json:
|
||||
continue
|
||||
|
||||
try:
|
||||
config = _load_config(config_json)
|
||||
if not isinstance(config, dict):
|
||||
continue
|
||||
migrated_config = migrate_pipeline_config(config)
|
||||
|
||||
# Only update if config changed
|
||||
if json.dumps(config, sort_keys=True) != json.dumps(migrated_config, sort_keys=True):
|
||||
_update_config(conn, table_name, pipeline_uuid, migrated_config)
|
||||
except Exception:
|
||||
# Skip invalid configs
|
||||
continue
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade is not supported for data migration."""
|
||||
# No downgrade - keep configs in new format
|
||||
pass
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
"""add_event_log_and_transcript_tables
|
||||
|
||||
Revision ID: 58846a8d7a81
|
||||
Revises: 0005_migrate_runner_config
|
||||
Create Date: 2026-05-23 15:41:47.030841
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers
|
||||
revision = '58846a8d7a81'
|
||||
down_revision = '0005_migrate_runner_config'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _table_exists(table_name: str) -> bool:
|
||||
return table_name in sa.inspect(op.get_bind()).get_table_names()
|
||||
|
||||
|
||||
def _index_exists(table_name: str, index_name: str) -> bool:
|
||||
return index_name in {index['name'] for index in sa.inspect(op.get_bind()).get_indexes(table_name)}
|
||||
|
||||
|
||||
def _column_exists(table_name: str, column_name: str) -> bool:
|
||||
return column_name in {column['name'] for column in sa.inspect(op.get_bind()).get_columns(table_name)}
|
||||
|
||||
|
||||
def _add_column_if_missing(table_name: str, column: sa.Column) -> None:
|
||||
if not _table_exists(table_name) or _column_exists(table_name, column.name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.add_column(column)
|
||||
|
||||
|
||||
def _create_index_if_missing(table_name: str, index_name: str, columns: list[str], *, unique: bool = False) -> None:
|
||||
if not _table_exists(table_name) or _index_exists(table_name, index_name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.create_index(index_name, columns, unique=unique)
|
||||
|
||||
|
||||
def _drop_index_if_exists(table_name: str, index_name: str) -> None:
|
||||
if not _table_exists(table_name) or not _index_exists(table_name, index_name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.drop_index(index_name)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create event_log table
|
||||
if not _table_exists('event_log'):
|
||||
op.create_table(
|
||||
'event_log',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('event_id', sa.String(255), nullable=False, unique=True),
|
||||
sa.Column('event_type', sa.String(100), nullable=False),
|
||||
sa.Column('event_time', sa.DateTime(), nullable=True),
|
||||
sa.Column('source', sa.String(50), nullable=False),
|
||||
sa.Column('bot_id', sa.String(255), nullable=True),
|
||||
sa.Column('workspace_id', sa.String(255), nullable=True),
|
||||
sa.Column('conversation_id', sa.String(255), nullable=True),
|
||||
sa.Column('thread_id', sa.String(255), nullable=True),
|
||||
sa.Column('actor_type', sa.String(50), nullable=True),
|
||||
sa.Column('actor_id', sa.String(255), nullable=True),
|
||||
sa.Column('actor_name', sa.String(255), nullable=True),
|
||||
sa.Column('subject_type', sa.String(50), nullable=True),
|
||||
sa.Column('subject_id', sa.String(255), nullable=True),
|
||||
sa.Column('input_summary', sa.Text(), nullable=True),
|
||||
sa.Column('input_json', sa.Text(), nullable=True),
|
||||
sa.Column('raw_ref', sa.String(255), nullable=True),
|
||||
sa.Column('run_id', sa.String(255), nullable=True),
|
||||
sa.Column('runner_id', sa.String(255), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
sa.Column('metadata_json', sa.Text(), nullable=True),
|
||||
)
|
||||
|
||||
# Create indexes for event_log
|
||||
_create_index_if_missing('event_log', 'ix_event_log_event_id', ['event_id'], unique=True)
|
||||
_create_index_if_missing('event_log', 'ix_event_log_event_type', ['event_type'])
|
||||
_create_index_if_missing('event_log', 'ix_event_log_bot_id', ['bot_id'])
|
||||
_create_index_if_missing('event_log', 'ix_event_log_conversation_id', ['conversation_id'])
|
||||
_create_index_if_missing('event_log', 'ix_event_log_run_id', ['run_id'])
|
||||
|
||||
# Create transcript table
|
||||
if not _table_exists('transcript'):
|
||||
op.create_table(
|
||||
'transcript',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('transcript_id', sa.String(255), nullable=False, unique=True),
|
||||
sa.Column('event_id', sa.String(255), nullable=False),
|
||||
sa.Column('bot_id', sa.String(255), nullable=True),
|
||||
sa.Column('workspace_id', sa.String(255), nullable=True),
|
||||
sa.Column('conversation_id', sa.String(255), nullable=False),
|
||||
sa.Column('thread_id', sa.String(255), nullable=True),
|
||||
sa.Column('role', sa.String(50), nullable=False),
|
||||
sa.Column('item_type', sa.String(50), nullable=False, server_default='message'),
|
||||
sa.Column('content', sa.Text(), nullable=True),
|
||||
sa.Column('content_json', sa.Text(), nullable=True),
|
||||
sa.Column('attachment_refs_json', sa.Text(), nullable=True),
|
||||
sa.Column('seq', sa.Integer(), nullable=False),
|
||||
sa.Column('run_id', sa.String(255), nullable=True),
|
||||
sa.Column('runner_id', sa.String(255), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
sa.Column('metadata_json', sa.Text(), nullable=True),
|
||||
)
|
||||
else:
|
||||
_add_column_if_missing('transcript', sa.Column('bot_id', sa.String(255), nullable=True))
|
||||
_add_column_if_missing('transcript', sa.Column('workspace_id', sa.String(255), nullable=True))
|
||||
|
||||
# Create indexes for transcript
|
||||
_create_index_if_missing('transcript', 'ix_transcript_transcript_id', ['transcript_id'], unique=True)
|
||||
_create_index_if_missing('transcript', 'ix_transcript_event_id', ['event_id'])
|
||||
_create_index_if_missing('transcript', 'ix_transcript_bot_id', ['bot_id'])
|
||||
_create_index_if_missing('transcript', 'ix_transcript_conversation_id', ['conversation_id'])
|
||||
_create_index_if_missing('transcript', 'ix_transcript_conversation_seq', ['conversation_id', 'seq'])
|
||||
_create_index_if_missing('transcript', 'ix_transcript_conversation_created', ['conversation_id', 'created_at'])
|
||||
_create_index_if_missing(
|
||||
'transcript',
|
||||
'ix_transcript_scope_seq',
|
||||
['bot_id', 'workspace_id', 'conversation_id', 'thread_id', 'seq'],
|
||||
)
|
||||
_create_index_if_missing('transcript', 'ix_transcript_run_id', ['run_id'])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop transcript table
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_run_id')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_scope_seq')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_conversation_created')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_conversation_seq')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_conversation_id')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_bot_id')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_event_id')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_transcript_id')
|
||||
|
||||
if _table_exists('transcript'):
|
||||
op.drop_table('transcript')
|
||||
|
||||
# Drop event_log table
|
||||
_drop_index_if_exists('event_log', 'ix_event_log_run_id')
|
||||
_drop_index_if_exists('event_log', 'ix_event_log_conversation_id')
|
||||
_drop_index_if_exists('event_log', 'ix_event_log_bot_id')
|
||||
_drop_index_if_exists('event_log', 'ix_event_log_event_type')
|
||||
_drop_index_if_exists('event_log', 'ix_event_log_event_id')
|
||||
|
||||
if _table_exists('event_log'):
|
||||
op.drop_table('event_log')
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
# Alembic script.py.mako — template for auto-generated revisions
|
||||
"""add agent_runner_state table for host-owned persistent state
|
||||
|
||||
Revision ID: 6dfd3dd7f0c7
|
||||
Revises: 58846a8d7a81
|
||||
Create Date: 2026-05-23 19:49:08.529110
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers
|
||||
revision = '6dfd3dd7f0c7'
|
||||
down_revision = '58846a8d7a81'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _table_exists(table_name: str) -> bool:
|
||||
return table_name in sa.inspect(op.get_bind()).get_table_names()
|
||||
|
||||
|
||||
def _index_exists(table_name: str, index_name: str) -> bool:
|
||||
return index_name in {index['name'] for index in sa.inspect(op.get_bind()).get_indexes(table_name)}
|
||||
|
||||
|
||||
def _create_index_if_missing(table_name: str, index_name: str, columns: list[str], *, unique: bool = False) -> None:
|
||||
if not _table_exists(table_name) or _index_exists(table_name, index_name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.create_index(index_name, columns, unique=unique)
|
||||
|
||||
|
||||
def _drop_index_if_exists(table_name: str, index_name: str) -> None:
|
||||
if not _table_exists(table_name) or not _index_exists(table_name, index_name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.drop_index(index_name)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
if not _table_exists('agent_runner_state'):
|
||||
op.create_table('agent_runner_state',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('runner_id', sa.String(length=255), nullable=False),
|
||||
sa.Column('binding_identity', sa.String(length=255), nullable=False),
|
||||
sa.Column('scope', sa.String(length=50), nullable=False),
|
||||
sa.Column('scope_key', sa.String(length=512), nullable=False),
|
||||
sa.Column('state_key', sa.String(length=255), nullable=False),
|
||||
sa.Column('value_json', sa.Text(), nullable=True),
|
||||
sa.Column('bot_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('workspace_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('conversation_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('thread_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('actor_type', sa.String(length=50), nullable=True),
|
||||
sa.Column('actor_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('subject_type', sa.String(length=50), nullable=True),
|
||||
sa.Column('subject_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('scope_key', 'state_key', name='uq_agent_runner_state_scope_key_state_key')
|
||||
)
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_actor_id', ['actor_id'])
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_binding_identity', ['binding_identity'])
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_bot_id', ['bot_id'])
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_conversation_id', ['conversation_id'])
|
||||
_create_index_if_missing(
|
||||
'agent_runner_state',
|
||||
'ix_agent_runner_state_runner_binding',
|
||||
['runner_id', 'binding_identity'],
|
||||
)
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_runner_id', ['runner_id'])
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_scope', ['scope'])
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_scope_key_lookup', ['scope_key'])
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_scope_key_lookup')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_scope')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_runner_id')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_runner_binding')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_conversation_id')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_bot_id')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_binding_identity')
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_actor_id')
|
||||
|
||||
if _table_exists('agent_runner_state'):
|
||||
op.drop_table('agent_runner_state')
|
||||
# ### end Alembic commands ###
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
"""add transcript scope columns
|
||||
|
||||
Revision ID: 7b2c1d9e4f30
|
||||
Revises: 6dfd3dd7f0c7
|
||||
Create Date: 2026-06-12
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '7b2c1d9e4f30'
|
||||
down_revision = '6dfd3dd7f0c7'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _table_exists(table_name: str) -> bool:
|
||||
return table_name in sa.inspect(op.get_bind()).get_table_names()
|
||||
|
||||
|
||||
def _column_exists(table_name: str, column_name: str) -> bool:
|
||||
return column_name in {column['name'] for column in sa.inspect(op.get_bind()).get_columns(table_name)}
|
||||
|
||||
|
||||
def _index_exists(table_name: str, index_name: str) -> bool:
|
||||
return index_name in {index['name'] for index in sa.inspect(op.get_bind()).get_indexes(table_name)}
|
||||
|
||||
|
||||
def _add_column_if_missing(table_name: str, column: sa.Column) -> None:
|
||||
if not _table_exists(table_name) or _column_exists(table_name, column.name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.add_column(column)
|
||||
|
||||
|
||||
def _create_index_if_missing(table_name: str, index_name: str, columns: list[str]) -> None:
|
||||
if not _table_exists(table_name) or _index_exists(table_name, index_name):
|
||||
return
|
||||
existing_columns = {column['name'] for column in sa.inspect(op.get_bind()).get_columns(table_name)}
|
||||
if not set(columns).issubset(existing_columns):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.create_index(index_name, columns)
|
||||
|
||||
|
||||
def _drop_index_if_exists(table_name: str, index_name: str) -> None:
|
||||
if not _table_exists(table_name) or not _index_exists(table_name, index_name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.drop_index(index_name)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
_add_column_if_missing('transcript', sa.Column('bot_id', sa.String(255), nullable=True))
|
||||
_add_column_if_missing('transcript', sa.Column('workspace_id', sa.String(255), nullable=True))
|
||||
_create_index_if_missing('transcript', 'ix_transcript_bot_id', ['bot_id'])
|
||||
_create_index_if_missing(
|
||||
'transcript',
|
||||
'ix_transcript_scope_seq',
|
||||
['bot_id', 'workspace_id', 'conversation_id', 'thread_id', 'seq'],
|
||||
)
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_scope_key')
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_scope_key_lookup', ['scope_key'])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_drop_index_if_exists('agent_runner_state', 'ix_agent_runner_state_scope_key_lookup')
|
||||
_create_index_if_missing('agent_runner_state', 'ix_agent_runner_state_scope_key', ['scope_key'])
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_scope_seq')
|
||||
_drop_index_if_exists('transcript', 'ix_transcript_bot_id')
|
||||
if not _table_exists('transcript'):
|
||||
return
|
||||
existing_columns = {column['name'] for column in sa.inspect(op.get_bind()).get_columns('transcript')}
|
||||
with op.batch_alter_table('transcript', schema=None) as batch_op:
|
||||
if 'workspace_id' in existing_columns:
|
||||
batch_op.drop_column('workspace_id')
|
||||
if 'bot_id' in existing_columns:
|
||||
batch_op.drop_column('bot_id')
|
||||
@@ -0,0 +1,202 @@
|
||||
"""add agent run ledger
|
||||
|
||||
Revision ID: 8d3a1f2c4b6e
|
||||
Revises: 7b2c1d9e4f30
|
||||
Create Date: 2026-06-15
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '8d3a1f2c4b6e'
|
||||
down_revision = '7b2c1d9e4f30'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _table_exists(table_name: str) -> bool:
|
||||
return table_name in sa.inspect(op.get_bind()).get_table_names()
|
||||
|
||||
|
||||
def _index_exists(table_name: str, index_name: str) -> bool:
|
||||
return index_name in {index['name'] for index in sa.inspect(op.get_bind()).get_indexes(table_name)}
|
||||
|
||||
|
||||
def _column_exists(table_name: str, column_name: str) -> bool:
|
||||
if not _table_exists(table_name):
|
||||
return False
|
||||
return column_name in {column['name'] for column in sa.inspect(op.get_bind()).get_columns(table_name)}
|
||||
|
||||
|
||||
def _add_column_if_missing(table_name: str, column: sa.Column) -> None:
|
||||
if not _table_exists(table_name) or _column_exists(table_name, column.name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.add_column(column)
|
||||
|
||||
|
||||
def _create_index_if_missing(table_name: str, index_name: str, columns: list[str], *, unique: bool = False) -> None:
|
||||
if not _table_exists(table_name) or _index_exists(table_name, index_name):
|
||||
return
|
||||
existing_columns = {column['name'] for column in sa.inspect(op.get_bind()).get_columns(table_name)}
|
||||
if not set(columns).issubset(existing_columns):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.create_index(index_name, columns, unique=unique)
|
||||
|
||||
|
||||
def _drop_index_if_exists(table_name: str, index_name: str) -> None:
|
||||
if not _table_exists(table_name) or not _index_exists(table_name, index_name):
|
||||
return
|
||||
with op.batch_alter_table(table_name, schema=None) as batch_op:
|
||||
batch_op.drop_index(index_name)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
if not _table_exists('agent_run'):
|
||||
op.create_table(
|
||||
'agent_run',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('run_id', sa.String(255), nullable=False, unique=True),
|
||||
sa.Column('event_id', sa.String(255), nullable=True),
|
||||
sa.Column('agent_id', sa.String(255), nullable=True),
|
||||
sa.Column('binding_id', sa.String(255), nullable=True),
|
||||
sa.Column('runner_id', sa.String(255), nullable=False),
|
||||
sa.Column('conversation_id', sa.String(255), nullable=True),
|
||||
sa.Column('thread_id', sa.String(255), nullable=True),
|
||||
sa.Column('workspace_id', sa.String(255), nullable=True),
|
||||
sa.Column('bot_id', sa.String(255), nullable=True),
|
||||
sa.Column('status', sa.String(50), nullable=False),
|
||||
sa.Column('status_reason', sa.Text(), nullable=True),
|
||||
sa.Column('queue_name', sa.String(255), nullable=True),
|
||||
sa.Column('priority', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('requested_runtime_id', sa.String(255), nullable=True),
|
||||
sa.Column('claimed_by_runtime_id', sa.String(255), nullable=True),
|
||||
sa.Column('claim_token', sa.String(255), nullable=True),
|
||||
sa.Column('claim_lease_expires_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('dispatch_attempts', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('last_claimed_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
sa.Column('started_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('finished_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
sa.Column('deadline_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('cancel_requested_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('usage_json', sa.Text(), nullable=True),
|
||||
sa.Column('cost_json', sa.Text(), nullable=True),
|
||||
sa.Column('authorization_json', sa.Text(), nullable=True),
|
||||
sa.Column('metadata_json', sa.Text(), nullable=True),
|
||||
)
|
||||
else:
|
||||
_add_column_if_missing('agent_run', sa.Column('queue_name', sa.String(255), nullable=True))
|
||||
_add_column_if_missing(
|
||||
'agent_run', sa.Column('priority', sa.Integer(), nullable=False, server_default='0')
|
||||
)
|
||||
_add_column_if_missing('agent_run', sa.Column('requested_runtime_id', sa.String(255), nullable=True))
|
||||
_add_column_if_missing('agent_run', sa.Column('claimed_by_runtime_id', sa.String(255), nullable=True))
|
||||
_add_column_if_missing('agent_run', sa.Column('claim_token', sa.String(255), nullable=True))
|
||||
_add_column_if_missing('agent_run', sa.Column('claim_lease_expires_at', sa.DateTime(), nullable=True))
|
||||
_add_column_if_missing(
|
||||
'agent_run', sa.Column('dispatch_attempts', sa.Integer(), nullable=False, server_default='0')
|
||||
)
|
||||
_add_column_if_missing('agent_run', sa.Column('last_claimed_at', sa.DateTime(), nullable=True))
|
||||
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_run_id', ['run_id'], unique=True)
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_event_id', ['event_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_binding_id', ['binding_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_runner_id', ['runner_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_conversation_id', ['conversation_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_bot_id', ['bot_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_status', ['status'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_queue_name', ['queue_name'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_requested_runtime_id', ['requested_runtime_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_claimed_by_runtime_id', ['claimed_by_runtime_id'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_claim_token', ['claim_token'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_claim_lease_expires_at', ['claim_lease_expires_at'])
|
||||
_create_index_if_missing(
|
||||
'agent_run',
|
||||
'ix_agent_run_scope_status',
|
||||
['bot_id', 'workspace_id', 'conversation_id', 'thread_id', 'status'],
|
||||
)
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_runner_status', ['runner_id', 'status'])
|
||||
_create_index_if_missing('agent_run', 'ix_agent_run_queue_claim', ['queue_name', 'status', 'priority', 'id'])
|
||||
|
||||
if not _table_exists('agent_run_event'):
|
||||
op.create_table(
|
||||
'agent_run_event',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('run_id', sa.String(255), nullable=False),
|
||||
sa.Column('sequence', sa.Integer(), nullable=False),
|
||||
sa.Column('type', sa.String(100), nullable=False),
|
||||
sa.Column('data_json', sa.Text(), nullable=True),
|
||||
sa.Column('usage_json', sa.Text(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
sa.Column('source', sa.String(50), nullable=True),
|
||||
sa.Column('metadata_json', sa.Text(), nullable=True),
|
||||
sa.UniqueConstraint('run_id', 'sequence', name='uq_agent_run_event_run_sequence'),
|
||||
)
|
||||
|
||||
_create_index_if_missing('agent_run_event', 'ix_agent_run_event_run_id', ['run_id'])
|
||||
_create_index_if_missing('agent_run_event', 'ix_agent_run_event_type', ['type'])
|
||||
_create_index_if_missing(
|
||||
'agent_run_event',
|
||||
'ix_agent_run_event_run_sequence',
|
||||
['run_id', 'sequence'],
|
||||
)
|
||||
|
||||
if not _table_exists('agent_runtime'):
|
||||
op.create_table(
|
||||
'agent_runtime',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('runtime_id', sa.String(255), nullable=False, unique=True),
|
||||
sa.Column('status', sa.String(50), nullable=False),
|
||||
sa.Column('display_name', sa.String(255), nullable=True),
|
||||
sa.Column('endpoint', sa.String(1024), nullable=True),
|
||||
sa.Column('version', sa.String(255), nullable=True),
|
||||
sa.Column('capabilities_json', sa.Text(), nullable=True),
|
||||
sa.Column('labels_json', sa.Text(), nullable=True),
|
||||
sa.Column('metadata_json', sa.Text(), nullable=True),
|
||||
sa.Column('last_heartbeat_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('heartbeat_deadline_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('(CURRENT_TIMESTAMP)')),
|
||||
)
|
||||
|
||||
_create_index_if_missing('agent_runtime', 'ix_agent_runtime_runtime_id', ['runtime_id'], unique=True)
|
||||
_create_index_if_missing('agent_runtime', 'ix_agent_runtime_status', ['status'])
|
||||
_create_index_if_missing('agent_runtime', 'ix_agent_runtime_last_heartbeat_at', ['last_heartbeat_at'])
|
||||
_create_index_if_missing('agent_runtime', 'ix_agent_runtime_heartbeat_deadline_at', ['heartbeat_deadline_at'])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_drop_index_if_exists('agent_runtime', 'ix_agent_runtime_heartbeat_deadline_at')
|
||||
_drop_index_if_exists('agent_runtime', 'ix_agent_runtime_last_heartbeat_at')
|
||||
_drop_index_if_exists('agent_runtime', 'ix_agent_runtime_status')
|
||||
_drop_index_if_exists('agent_runtime', 'ix_agent_runtime_runtime_id')
|
||||
if _table_exists('agent_runtime'):
|
||||
op.drop_table('agent_runtime')
|
||||
|
||||
_drop_index_if_exists('agent_run_event', 'ix_agent_run_event_run_sequence')
|
||||
_drop_index_if_exists('agent_run_event', 'ix_agent_run_event_type')
|
||||
_drop_index_if_exists('agent_run_event', 'ix_agent_run_event_run_id')
|
||||
if _table_exists('agent_run_event'):
|
||||
op.drop_table('agent_run_event')
|
||||
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_queue_claim')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_claim_lease_expires_at')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_claim_token')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_claimed_by_runtime_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_requested_runtime_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_queue_name')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_runner_status')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_scope_status')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_status')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_bot_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_conversation_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_runner_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_binding_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_event_id')
|
||||
_drop_index_if_exists('agent_run', 'ix_agent_run_run_id')
|
||||
if _table_exists('agent_run'):
|
||||
op.drop_table('agent_run')
|
||||
@@ -11,6 +11,7 @@ from ...entity.persistence import (
|
||||
pipeline as persistence_pipeline,
|
||||
bot as persistence_bot,
|
||||
)
|
||||
from ...agent.runner.config_migration import LEGACY_RUNNER_ID_MAP
|
||||
|
||||
|
||||
@migration.migration_class(1)
|
||||
@@ -114,21 +115,28 @@ class DBMigrateV3Config(migration.DBMigration):
|
||||
pipeline_config = default_pipeline['config']
|
||||
|
||||
# ai
|
||||
pipeline_config['ai']['runner'] = {
|
||||
'runner': self.ap.provider_cfg.data['runner'],
|
||||
ai_config = pipeline_config.setdefault('ai', {})
|
||||
runner_name = self.ap.provider_cfg.data['runner']
|
||||
runner_id = LEGACY_RUNNER_ID_MAP.get(runner_name, '')
|
||||
ai_config['runner'] = {
|
||||
'id': runner_id,
|
||||
}
|
||||
pipeline_config['ai']['local-agent']['model'] = model_uuid
|
||||
pipeline_config['ai']['local-agent']['max-round'] = self.ap.pipeline_cfg.data['msg-truncate']['round'][
|
||||
'max-round'
|
||||
]
|
||||
runner_configs = ai_config.setdefault('runner_config', {})
|
||||
|
||||
pipeline_config['ai']['local-agent']['prompt'] = [
|
||||
local_agent_runner_id = LEGACY_RUNNER_ID_MAP['local-agent']
|
||||
local_agent_config = runner_configs.setdefault(local_agent_runner_id, {})
|
||||
local_agent_config['model'] = {
|
||||
'primary': model_uuid,
|
||||
'fallbacks': [],
|
||||
}
|
||||
|
||||
local_agent_config['prompt'] = [
|
||||
{
|
||||
'role': 'system',
|
||||
'content': self.ap.provider_cfg.data['prompt']['default'],
|
||||
}
|
||||
]
|
||||
pipeline_config['ai']['dify-service-api'] = {
|
||||
runner_configs[LEGACY_RUNNER_ID_MAP['dify-service-api']] = {
|
||||
'base-url': self.ap.provider_cfg.data['dify-service-api']['base-url'],
|
||||
'app-type': self.ap.provider_cfg.data['dify-service-api']['app-type'],
|
||||
'api-key': self.ap.provider_cfg.data['dify-service-api'][
|
||||
@@ -139,7 +147,7 @@ class DBMigrateV3Config(migration.DBMigration):
|
||||
self.ap.provider_cfg.data['dify-service-api']['app-type']
|
||||
]['timeout'],
|
||||
}
|
||||
pipeline_config['ai']['dashscope-app-api'] = {
|
||||
runner_configs[LEGACY_RUNNER_ID_MAP['dashscope-app-api']] = {
|
||||
'app-type': self.ap.provider_cfg.data['dashscope-app-api']['app-type'],
|
||||
'api-key': self.ap.provider_cfg.data['dashscope-app-api']['api-key'],
|
||||
'references_quote': self.ap.provider_cfg.data['dashscope-app-api'][
|
||||
|
||||
Reference in New Issue
Block a user