Commit 952bece6 authored by takatost's avatar takatost

add manual convert logic

parent 7fb22f09
import base64
import json
import logging
import secrets
import click
......@@ -13,12 +12,11 @@ from extensions.ext_database import db
from libs.helper import email as email_validate
from libs.password import hash_password, password_pattern, valid_password
from libs.rsa import generate_key_pair
from models.account import Tenant, TenantAccountJoin
from models.account import Tenant
from models.dataset import Dataset, DatasetCollectionBinding, DocumentSegment
from models.dataset import Document as DatasetDocument
from models.model import Account, App, AppMode, AppModelConfig, AppAnnotationSetting, Conversation, MessageAnnotation
from models.provider import Provider, ProviderModel
from services.workflow.workflow_converter import WorkflowConverter
@click.command('reset-password', help='Reset the account password.')
......@@ -384,10 +382,11 @@ def convert_to_agent_apps():
while True:
# fetch first 1000 apps
sql_query = """SELECT a.id AS id FROM apps a
INNER JOIN app_model_configs am ON a.app_model_config_id=am.id
WHERE a.mode = 'chat' AND am.agent_mode is not null
and (am.agent_mode like '%"strategy": "function_call"%' or am.agent_mode like '%"strategy": "react"%')
and am.agent_mode like '{"enabled": true%' ORDER BY a.created_at DESC LIMIT 1000"""
INNER JOIN app_model_configs am ON a.app_model_config_id=am.id
WHERE a.mode = 'chat' AND am.agent_mode is not null
and (am.agent_mode like '%"strategy": "function_call"%' or am.agent_mode like '%"strategy": "react"%')
and am.agent_mode like '{"enabled": true%' ORDER BY a.created_at DESC LIMIT 1000"""
with db.engine.begin() as conn:
rs = conn.execute(db.text(sql_query))
......@@ -424,77 +423,9 @@ and am.agent_mode like '{"enabled": true%' ORDER BY a.created_at DESC LIMIT 1000
click.echo(click.style('Congratulations! Converted {} agent apps.'.format(len(proceeded_app_ids)), fg='green'))
@click.command('convert-to-workflow-chatbot-apps', help='Convert Basic Export Assistant to Chatbot Workflow App.')
def convert_to_workflow_chatbot_apps():
"""
Convert Basic Export Assistant to Chatbot Workflow App.
"""
click.echo(click.style('Start convert to workflow chatbot apps.', fg='green'))
proceeded_app_ids = []
workflow_converter = WorkflowConverter()
while True:
# fetch first 1000 apps
sql_query = """SELECT a.id FROM apps a
LEFT JOIN app_model_configs am ON a.app_model_config_id=am.id
WHERE a.mode = 'chat' AND am.prompt_type='advanced' ORDER BY a.created_at DESC LIMIT 1000"""
with db.engine.begin() as conn:
rs = conn.execute(db.text(sql_query))
apps = []
for i in rs:
app_id = str(i.id)
print(app_id)
if app_id not in proceeded_app_ids:
proceeded_app_ids.append(app_id)
app = db.session.query(App).filter(App.id == app_id).first()
apps.append(app)
if len(apps) == 0:
break
for app in apps:
click.echo('Converting app: {}'.format(app.id))
try:
# get workspace of app
tenant = db.session.query(Tenant).filter(Tenant.id == app.tenant_id).first()
if not tenant:
click.echo(click.style('Tenant not found: {}'.format(app.tenant_id), fg='red'))
continue
# get workspace owner
tenant_account_join = db.session.query(TenantAccountJoin).filter(
TenantAccountJoin.tenant_id == tenant.id,
TenantAccountJoin.role == 'owner'
).first()
if not tenant_account_join:
click.echo(click.style('Tenant owner not found: {}'.format(tenant.id), fg='red'))
continue
# convert to workflow
workflow_converter.convert_to_workflow(
app_model=app,
account_id=tenant_account_join.account_id
)
click.echo(click.style('Converted app: {}'.format(app.id), fg='green'))
except Exception as e:
logging.exception('Convert app error: {}'.format(app.id))
click.echo(
click.style('Convert app error: {} {}'.format(e.__class__.__name__,
str(e)), fg='red'))
click.echo(click.style('Congratulations! Converted {} workflow chatbot apps.'.format(len(proceeded_app_ids)), fg='green'))
def register_commands(app):
app.cli.add_command(reset_password)
app.cli.add_command(reset_email)
app.cli.add_command(reset_encrypt_key_pair)
app.cli.add_command(vdb_migrate)
app.cli.add_command(convert_to_agent_apps)
app.cli.add_command(convert_to_workflow_chatbot_apps)
......@@ -69,15 +69,15 @@ class ConvertToWorkflowApi(Resource):
@setup_required
@login_required
@account_initialization_required
@get_app_model(mode=AppMode.CHAT)
@marshal_with(workflow_fields)
@get_app_model(mode=[AppMode.CHAT, AppMode.COMPLETION])
def post(self, app_model: App):
"""
Convert basic mode of chatbot app to workflow
Convert basic mode of chatbot app(expert mode) to workflow mode
Convert Completion App to Workflow App
"""
# convert to workflow mode
workflow_service = WorkflowService()
workflow = workflow_service.chatbot_convert_to_workflow(
workflow = workflow_service.convert_to_workflow(
app_model=app_model,
account=current_user
)
......
......@@ -53,6 +53,7 @@ def upgrade():
sa.Column('elapsed_time', sa.Float(), server_default=sa.text('0'), nullable=False),
sa.Column('execution_metadata', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP(0)'), nullable=False),
sa.Column('created_by_role', sa.String(length=255), nullable=False),
sa.Column('created_by', postgresql.UUID(), nullable=False),
sa.Column('finished_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id', name='workflow_node_execution_pkey')
......@@ -80,6 +81,7 @@ def upgrade():
sa.Column('total_price', sa.Numeric(precision=10, scale=7), nullable=True),
sa.Column('currency', sa.String(length=255), nullable=True),
sa.Column('total_steps', sa.Integer(), server_default=sa.text('0'), nullable=True),
sa.Column('created_by_role', sa.String(length=255), nullable=False),
sa.Column('created_by', postgresql.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP(0)'), nullable=False),
sa.Column('finished_at', sa.DateTime(), nullable=True),
......
......@@ -28,6 +28,7 @@ class DifySetup(db.Model):
class AppMode(Enum):
COMPLETION = 'completion'
WORKFLOW = 'workflow'
CHAT = 'chat'
AGENT = 'agent'
......
......@@ -7,6 +7,27 @@ from extensions.ext_database import db
from models.account import Account
class CreatedByRole(Enum):
"""
Created By Role Enum
"""
ACCOUNT = 'account'
END_USER = 'end_user'
@classmethod
def value_of(cls, value: str) -> 'CreatedByRole':
"""
Get value of given mode.
:param value: mode value
:return: mode
"""
for mode in cls:
if mode.value == value:
return mode
raise ValueError(f'invalid created by role value {value}')
class WorkflowType(Enum):
"""
Workflow Type Enum
......@@ -99,6 +120,49 @@ class Workflow(db.Model):
return Account.query.get(self.updated_by)
class WorkflowRunTriggeredFrom(Enum):
"""
Workflow Run Triggered From Enum
"""
DEBUGGING = 'debugging'
APP_RUN = 'app-run'
@classmethod
def value_of(cls, value: str) -> 'WorkflowRunTriggeredFrom':
"""
Get value of given mode.
:param value: mode value
:return: mode
"""
for mode in cls:
if mode.value == value:
return mode
raise ValueError(f'invalid workflow run triggered from value {value}')
class WorkflowRunStatus(Enum):
"""
Workflow Run Status Enum
"""
RUNNING = 'running'
SUCCEEDED = 'succeeded'
FAILED = 'failed'
@classmethod
def value_of(cls, value: str) -> 'WorkflowRunStatus':
"""
Get value of given mode.
:param value: mode value
:return: mode
"""
for mode in cls:
if mode.value == value:
return mode
raise ValueError(f'invalid workflow run status value {value}')
class WorkflowRun(db.Model):
"""
Workflow Run
......@@ -128,6 +192,12 @@ class WorkflowRun(db.Model):
- total_price (decimal) `optional` Total cost
- currency (string) `optional` Currency, such as USD / RMB
- total_steps (int) Total steps (redundant), default 0
- created_by_role (string) Creator role
- `account` Console account
- `end_user` End user
- created_by (uuid) Runner ID
- created_at (timestamp) Run time
- finished_at (timestamp) End time
......@@ -157,6 +227,7 @@ class WorkflowRun(db.Model):
total_price = db.Column(db.Numeric(10, 7))
currency = db.Column(db.String(255))
total_steps = db.Column(db.Integer, server_default=db.text('0'))
created_by_role = db.Column(db.String(255), nullable=False)
created_by = db.Column(UUID, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
finished_at = db.Column(db.DateTime)
......@@ -208,6 +279,12 @@ class WorkflowNodeExecution(db.Model):
- currency (string) `optional` Currency, such as USD / RMB
- created_at (timestamp) Run time
- created_by_role (string) Creator role
- `account` Console account
- `end_user` End user
- created_by (uuid) Runner ID
- finished_at (timestamp) End time
"""
......@@ -240,6 +317,7 @@ class WorkflowNodeExecution(db.Model):
elapsed_time = db.Column(db.Float, nullable=False, server_default=db.text('0'))
execution_metadata = db.Column(db.Text)
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
created_by_role = db.Column(db.String(255), nullable=False)
created_by = db.Column(UUID, nullable=False)
finished_at = db.Column(db.DateTime)
......
......@@ -17,9 +17,11 @@ from core.model_runtime.utils.encoders import jsonable_encoder
from core.prompt.simple_prompt_transform import SimplePromptTransform
from core.workflow.entities.NodeEntities import NodeType
from core.workflow.nodes.end.entities import EndNodeOutputType
from events.app_event import app_was_created
from extensions.ext_database import db
from models.account import Account
from models.api_based_extension import APIBasedExtension, APIBasedExtensionPoint
from models.model import App, AppMode, ChatbotAppEngine
from models.model import App, AppMode, ChatbotAppEngine, AppModelConfig, Site
from models.workflow import Workflow, WorkflowType
......@@ -28,26 +30,99 @@ class WorkflowConverter:
App Convert to Workflow Mode
"""
def convert_to_workflow(self, app_model: App, account_id: str) -> Workflow:
def convert_to_workflow(self, app_model: App, account: Account) -> App:
"""
Convert to workflow mode
Convert app to workflow
- basic mode of chatbot app
- advanced mode of assistant app (for migration)
- advanced mode of assistant app
- completion app (for migration)
- completion app
:param app_model: App instance
:param account: Account
:return: new App instance
"""
# get original app config
app_model_config = app_model.app_model_config
# convert app model config
workflow = self.convert_app_model_config_to_workflow(
app_model=app_model,
app_model_config=app_model_config,
account_id=account.id
)
# create new app
new_app = App()
new_app.tenant_id = app_model.tenant_id
new_app.name = app_model.name + '(workflow)'
new_app.mode = AppMode.CHAT.value \
if app_model.mode == AppMode.CHAT.value else AppMode.WORKFLOW.value
new_app.icon = app_model.icon
new_app.icon_background = app_model.icon_background
new_app.enable_site = app_model.enable_site
new_app.enable_api = app_model.enable_api
new_app.api_rpm = app_model.api_rpm
new_app.api_rph = app_model.api_rph
new_app.is_demo = False
new_app.is_public = app_model.is_public
db.session.add(new_app)
db.session.flush()
# create new app model config record
new_app_model_config = app_model_config.copy()
new_app_model_config.id = None
new_app_model_config.app_id = new_app.id
new_app_model_config.external_data_tools = ''
new_app_model_config.model = ''
new_app_model_config.user_input_form = ''
new_app_model_config.dataset_query_variable = None
new_app_model_config.pre_prompt = None
new_app_model_config.agent_mode = ''
new_app_model_config.prompt_type = 'simple'
new_app_model_config.chat_prompt_config = ''
new_app_model_config.completion_prompt_config = ''
new_app_model_config.dataset_configs = ''
new_app_model_config.chatbot_app_engine = ChatbotAppEngine.WORKFLOW.value \
if app_model.mode == AppMode.CHAT.value else ChatbotAppEngine.NORMAL.value
new_app_model_config.workflow_id = workflow.id
db.session.add(new_app_model_config)
db.session.flush()
new_app.app_model_config_id = new_app_model_config.id
db.session.commit()
site = Site(
app_id=new_app.id,
title=new_app.name,
default_language=account.interface_language,
customize_token_strategy='not_allow',
code=Site.generate_code(16)
)
db.session.add(site)
db.session.commit()
app_was_created.send(new_app)
return new_app
def convert_app_model_config_to_workflow(self, app_model: App,
app_model_config: AppModelConfig,
account_id: str) -> Workflow:
"""
Convert app model config to workflow mode
:param app_model: App instance
:param app_model_config: AppModelConfig instance
:param account_id: Account ID
:return: workflow instance
:return:
"""
# get new app mode
new_app_mode = self._get_new_app_mode(app_model)
# get original app config
app_model_config = app_model.app_model_config
# convert app model config
application_manager = ApplicationManager()
app_orchestration_config_entity = application_manager.convert_from_app_model_config_dict(
......@@ -122,33 +197,11 @@ class WorkflowConverter:
type=WorkflowType.from_app_mode(new_app_mode).value,
version='draft',
graph=json.dumps(graph),
created_by=account_id
created_by=account_id,
created_at=app_model_config.created_at
)
db.session.add(workflow)
db.session.flush()
# create new app model config record
new_app_model_config = app_model_config.copy()
new_app_model_config.id = None
new_app_model_config.external_data_tools = ''
new_app_model_config.model = ''
new_app_model_config.user_input_form = ''
new_app_model_config.dataset_query_variable = None
new_app_model_config.pre_prompt = None
new_app_model_config.agent_mode = ''
new_app_model_config.prompt_type = 'simple'
new_app_model_config.chat_prompt_config = ''
new_app_model_config.completion_prompt_config = ''
new_app_model_config.dataset_configs = ''
new_app_model_config.chatbot_app_engine = ChatbotAppEngine.WORKFLOW.value \
if new_app_mode == AppMode.CHAT else ChatbotAppEngine.NORMAL.value
new_app_model_config.workflow_id = workflow.id
db.session.add(new_app_model_config)
db.session.commit()
app_model.app_model_config_id = new_app_model_config.id
db.session.commit()
return workflow
......@@ -469,7 +522,7 @@ class WorkflowConverter:
"type": NodeType.END.value,
}
}
elif app_model.mode == "completion":
elif app_model.mode == AppMode.COMPLETION.value:
# for original completion app
return {
"id": "end",
......@@ -516,7 +569,7 @@ class WorkflowConverter:
:param app_model: App instance
:return: AppMode
"""
if app_model.mode == "completion":
if app_model.mode == AppMode.COMPLETION.value:
return AppMode.WORKFLOW
else:
return AppMode.value_of(app_model.mode)
......
......@@ -3,7 +3,7 @@ from datetime import datetime
from extensions.ext_database import db
from models.account import Account
from models.model import App, ChatbotAppEngine
from models.model import App, ChatbotAppEngine, AppMode
from models.workflow import Workflow, WorkflowType
from services.workflow.defaults import default_block_configs
from services.workflow.workflow_converter import WorkflowConverter
......@@ -65,20 +65,29 @@ class WorkflowService:
# return default block config
return default_block_configs
def chatbot_convert_to_workflow(self, app_model: App, account: Account) -> Workflow:
def convert_to_workflow(self, app_model: App, account: Account) -> App:
"""
basic mode of chatbot app to workflow
Basic mode of chatbot app(expert mode) to workflow
Completion App to Workflow App
:param app_model: App instance
:param account: Account instance
:return:
"""
# check if chatbot app is in basic mode
if app_model.app_model_config.chatbot_app_engine != ChatbotAppEngine.NORMAL:
raise ValueError('Chatbot app already in workflow mode')
# convert to workflow mode
# chatbot convert to workflow mode
workflow_converter = WorkflowConverter()
workflow = workflow_converter.convert_to_workflow(app_model=app_model, account_id=account.id)
return workflow
if app_model.mode == AppMode.CHAT.value:
# check if chatbot app is in basic mode
if app_model.app_model_config.chatbot_app_engine != ChatbotAppEngine.NORMAL:
raise ValueError('Chatbot app already in workflow mode')
elif app_model.mode != AppMode.COMPLETION.value:
raise ValueError(f'Current App mode: {app_model.mode} is not supported convert to workflow.')
# convert to workflow
new_app = workflow_converter.convert_to_workflow(
app_model=app_model,
account=account
)
return new_app
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment