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:
huanghuoguoguo
2026-06-22 23:06:45 +08:00
140 changed files with 27340 additions and 6253 deletions
@@ -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
@@ -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')
@@ -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 ###
@@ -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'][