Team Ai
Apppublic

Underground-Digital/Workflow-Engine

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
model.py1665 linesDownload Raw Back to models
1import json2import re3import uuid4from collections.abc import Mapping5from datetime import datetime6from enum import Enum7from typing import Any, Literal, Optional8 9import sqlalchemy as sa10from flask import request11from flask_login import UserMixin12from sqlalchemy import Float, func, text13from sqlalchemy.orm import Mapped, mapped_column14 15from configs import dify_config16from core.file import FILE_MODEL_IDENTITY, File, FileExtraConfig, FileTransferMethod, FileType17from core.file import helpers as file_helpers18from core.file.tool_file_parser import ToolFileParser19from extensions.ext_database import db20from libs.helper import generate_string21from models.enums import CreatedByRole22 23from .account import Account, Tenant24from .types import StringUUID25 26 27class DifySetup(db.Model):28    __tablename__ = "dify_setups"29    __table_args__ = (db.PrimaryKeyConstraint("version", name="dify_setup_pkey"),)30 31    version = db.Column(db.String(255), nullable=False)32    setup_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))33 34 35class AppMode(str, Enum):36    COMPLETION = "completion"37    WORKFLOW = "workflow"38    CHAT = "chat"39    ADVANCED_CHAT = "advanced-chat"40    AGENT_CHAT = "agent-chat"41    CHANNEL = "channel"42 43    @classmethod44    def value_of(cls, value: str) -> "AppMode":45        """46        Get value of given mode.47 48        :param value: mode value49        :return: mode50        """51        for mode in cls:52            if mode.value == value:53                return mode54        raise ValueError(f"invalid mode value {value}")55 56 57class IconType(Enum):58    IMAGE = "image"59    EMOJI = "emoji"60 61 62class App(db.Model):63    __tablename__ = "apps"64    __table_args__ = (db.PrimaryKeyConstraint("id", name="app_pkey"), db.Index("app_tenant_id_idx", "tenant_id"))65 66    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))67    tenant_id: Mapped[str] = db.Column(StringUUID, nullable=False)68    name = db.Column(db.String(255), nullable=False)69    description = db.Column(db.Text, nullable=False, server_default=db.text("''::character varying"))70    mode = db.Column(db.String(255), nullable=False)71    icon_type = db.Column(db.String(255), nullable=True)72    icon = db.Column(db.String(255))73    icon_background = db.Column(db.String(255))74    app_model_config_id = db.Column(StringUUID, nullable=True)75    workflow_id = db.Column(StringUUID, nullable=True)76    status = db.Column(db.String(255), nullable=False, server_default=db.text("'normal'::character varying"))77    enable_site = db.Column(db.Boolean, nullable=False)78    enable_api = db.Column(db.Boolean, nullable=False)79    api_rpm = db.Column(db.Integer, nullable=False, server_default=db.text("0"))80    api_rph = db.Column(db.Integer, nullable=False, server_default=db.text("0"))81    is_demo = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))82    is_public = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))83    is_universal = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))84    tracing = db.Column(db.Text, nullable=True)85    max_active_requests = db.Column(db.Integer, nullable=True)86    created_by = db.Column(StringUUID, nullable=True)87    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))88    updated_by = db.Column(StringUUID, nullable=True)89    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))90    use_icon_as_answer_icon = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))91 92    @property93    def desc_or_prompt(self):94        if self.description:95            return self.description96        else:97            app_model_config = self.app_model_config98            if app_model_config:99                return app_model_config.pre_prompt100            else:101                return ""102 103    @property104    def site(self):105        site = db.session.query(Site).filter(Site.app_id == self.id).first()106        return site107 108    @property109    def app_model_config(self):110        if self.app_model_config_id:111            return db.session.query(AppModelConfig).filter(AppModelConfig.id == self.app_model_config_id).first()112 113        return None114 115    @property116    def workflow(self) -> Optional["Workflow"]:117        if self.workflow_id:118            from .workflow import Workflow119 120            return db.session.query(Workflow).filter(Workflow.id == self.workflow_id).first()121 122        return None123 124    @property125    def api_base_url(self):126        return (dify_config.SERVICE_API_URL or request.host_url.rstrip("/")) + "/v1"127 128    @property129    def tenant(self):130        tenant = db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first()131        return tenant132 133    @property134    def is_agent(self) -> bool:135        app_model_config = self.app_model_config136        if not app_model_config:137            return False138        if not app_model_config.agent_mode:139            return False140        if self.app_model_config.agent_mode_dict.get("enabled", False) and self.app_model_config.agent_mode_dict.get(141            "strategy", ""142        ) in {"function_call", "react"}:143            self.mode = AppMode.AGENT_CHAT.value144            db.session.commit()145            return True146        return False147 148    @property149    def mode_compatible_with_agent(self) -> str:150        if self.mode == AppMode.CHAT.value and self.is_agent:151            return AppMode.AGENT_CHAT.value152 153        return self.mode154 155    @property156    def deleted_tools(self) -> list:157        # get agent mode tools158        app_model_config = self.app_model_config159        if not app_model_config:160            return []161        if not app_model_config.agent_mode:162            return []163        agent_mode = app_model_config.agent_mode_dict164        tools = agent_mode.get("tools", [])165 166        provider_ids = []167 168        for tool in tools:169            keys = list(tool.keys())170            if len(keys) >= 4:171                provider_type = tool.get("provider_type", "")172                provider_id = tool.get("provider_id", "")173                if provider_type == "api":174                    # check if provider id is a uuid string, if not, skip175                    try:176                        uuid.UUID(provider_id)177                    except Exception:178                        continue179                    provider_ids.append(provider_id)180 181        if not provider_ids:182            return []183 184        api_providers = db.session.execute(185            text("SELECT id FROM tool_api_providers WHERE id IN :provider_ids"), {"provider_ids": tuple(provider_ids)}186        ).fetchall()187 188        deleted_tools = []189        current_api_provider_ids = [str(api_provider.id) for api_provider in api_providers]190 191        for tool in tools:192            keys = list(tool.keys())193            if len(keys) >= 4:194                provider_type = tool.get("provider_type", "")195                provider_id = tool.get("provider_id", "")196                if provider_type == "api" and provider_id not in current_api_provider_ids:197                    deleted_tools.append(tool["tool_name"])198 199        return deleted_tools200 201    @property202    def tags(self):203        tags = (204            db.session.query(Tag)205            .join(TagBinding, Tag.id == TagBinding.tag_id)206            .filter(207                TagBinding.target_id == self.id,208                TagBinding.tenant_id == self.tenant_id,209                Tag.tenant_id == self.tenant_id,210                Tag.type == "app",211            )212            .all()213        )214 215        return tags or []216 217 218class AppModelConfig(db.Model):219    __tablename__ = "app_model_configs"220    __table_args__ = (db.PrimaryKeyConstraint("id", name="app_model_config_pkey"), db.Index("app_app_id_idx", "app_id"))221 222    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))223    app_id = db.Column(StringUUID, nullable=False)224    provider = db.Column(db.String(255), nullable=True)225    model_id = db.Column(db.String(255), nullable=True)226    configs = db.Column(db.JSON, nullable=True)227    created_by = db.Column(StringUUID, nullable=True)228    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))229    updated_by = db.Column(StringUUID, nullable=True)230    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))231    opening_statement = db.Column(db.Text)232    suggested_questions = db.Column(db.Text)233    suggested_questions_after_answer = db.Column(db.Text)234    speech_to_text = db.Column(db.Text)235    text_to_speech = db.Column(db.Text)236    more_like_this = db.Column(db.Text)237    model = db.Column(db.Text)238    user_input_form = db.Column(db.Text)239    dataset_query_variable = db.Column(db.String(255))240    pre_prompt = db.Column(db.Text)241    agent_mode = db.Column(db.Text)242    sensitive_word_avoidance = db.Column(db.Text)243    retriever_resource = db.Column(db.Text)244    prompt_type = db.Column(db.String(255), nullable=False, server_default=db.text("'simple'::character varying"))245    chat_prompt_config = db.Column(db.Text)246    completion_prompt_config = db.Column(db.Text)247    dataset_configs = db.Column(db.Text)248    external_data_tools = db.Column(db.Text)249    file_upload = db.Column(db.Text)250 251    @property252    def app(self):253        app = db.session.query(App).filter(App.id == self.app_id).first()254        return app255 256    @property257    def model_dict(self) -> dict:258        return json.loads(self.model) if self.model else None259 260    @property261    def suggested_questions_list(self) -> list:262        return json.loads(self.suggested_questions) if self.suggested_questions else []263 264    @property265    def suggested_questions_after_answer_dict(self) -> dict:266        return (267            json.loads(self.suggested_questions_after_answer)268            if self.suggested_questions_after_answer269            else {"enabled": False}270        )271 272    @property273    def speech_to_text_dict(self) -> dict:274        return json.loads(self.speech_to_text) if self.speech_to_text else {"enabled": False}275 276    @property277    def text_to_speech_dict(self) -> dict:278        return json.loads(self.text_to_speech) if self.text_to_speech else {"enabled": False}279 280    @property281    def retriever_resource_dict(self) -> dict:282        return json.loads(self.retriever_resource) if self.retriever_resource else {"enabled": True}283 284    @property285    def annotation_reply_dict(self) -> dict:286        annotation_setting = (287            db.session.query(AppAnnotationSetting).filter(AppAnnotationSetting.app_id == self.app_id).first()288        )289        if annotation_setting:290            collection_binding_detail = annotation_setting.collection_binding_detail291            return {292                "id": annotation_setting.id,293                "enabled": True,294                "score_threshold": annotation_setting.score_threshold,295                "embedding_model": {296                    "embedding_provider_name": collection_binding_detail.provider_name,297                    "embedding_model_name": collection_binding_detail.model_name,298                },299            }300 301        else:302            return {"enabled": False}303 304    @property305    def more_like_this_dict(self) -> dict:306        return json.loads(self.more_like_this) if self.more_like_this else {"enabled": False}307 308    @property309    def sensitive_word_avoidance_dict(self) -> dict:310        return (311            json.loads(self.sensitive_word_avoidance)312            if self.sensitive_word_avoidance313            else {"enabled": False, "type": "", "configs": []}314        )315 316    @property317    def external_data_tools_list(self) -> list[dict]:318        return json.loads(self.external_data_tools) if self.external_data_tools else []319 320    @property321    def user_input_form_list(self) -> dict:322        return json.loads(self.user_input_form) if self.user_input_form else []323 324    @property325    def agent_mode_dict(self) -> dict:326        return (327            json.loads(self.agent_mode)328            if self.agent_mode329            else {"enabled": False, "strategy": None, "tools": [], "prompt": None}330        )331 332    @property333    def chat_prompt_config_dict(self) -> dict:334        return json.loads(self.chat_prompt_config) if self.chat_prompt_config else {}335 336    @property337    def completion_prompt_config_dict(self) -> dict:338        return json.loads(self.completion_prompt_config) if self.completion_prompt_config else {}339 340    @property341    def dataset_configs_dict(self) -> dict:342        if self.dataset_configs:343            dataset_configs = json.loads(self.dataset_configs)344            if "retrieval_model" not in dataset_configs:345                return {"retrieval_model": "single"}346            else:347                return dataset_configs348        return {349            "retrieval_model": "multiple",350        }351 352    @property353    def file_upload_dict(self) -> dict:354        return (355            json.loads(self.file_upload)356            if self.file_upload357            else {358                "image": {359                    "enabled": False,360                    "number_limits": 3,361                    "detail": "high",362                    "transfer_methods": ["remote_url", "local_file"],363                }364            }365        )366 367    def to_dict(self) -> dict:368        return {369            "opening_statement": self.opening_statement,370            "suggested_questions": self.suggested_questions_list,371            "suggested_questions_after_answer": self.suggested_questions_after_answer_dict,372            "speech_to_text": self.speech_to_text_dict,373            "text_to_speech": self.text_to_speech_dict,374            "retriever_resource": self.retriever_resource_dict,375            "annotation_reply": self.annotation_reply_dict,376            "more_like_this": self.more_like_this_dict,377            "sensitive_word_avoidance": self.sensitive_word_avoidance_dict,378            "external_data_tools": self.external_data_tools_list,379            "model": self.model_dict,380            "user_input_form": self.user_input_form_list,381            "dataset_query_variable": self.dataset_query_variable,382            "pre_prompt": self.pre_prompt,383            "agent_mode": self.agent_mode_dict,384            "prompt_type": self.prompt_type,385            "chat_prompt_config": self.chat_prompt_config_dict,386            "completion_prompt_config": self.completion_prompt_config_dict,387            "dataset_configs": self.dataset_configs_dict,388            "file_upload": self.file_upload_dict,389        }390 391    def from_model_config_dict(self, model_config: Mapping[str, Any]):392        self.opening_statement = model_config.get("opening_statement")393        self.suggested_questions = (394            json.dumps(model_config["suggested_questions"]) if model_config.get("suggested_questions") else None395        )396        self.suggested_questions_after_answer = (397            json.dumps(model_config["suggested_questions_after_answer"])398            if model_config.get("suggested_questions_after_answer")399            else None400        )401        self.speech_to_text = json.dumps(model_config["speech_to_text"]) if model_config.get("speech_to_text") else None402        self.text_to_speech = json.dumps(model_config["text_to_speech"]) if model_config.get("text_to_speech") else None403        self.more_like_this = json.dumps(model_config["more_like_this"]) if model_config.get("more_like_this") else None404        self.sensitive_word_avoidance = (405            json.dumps(model_config["sensitive_word_avoidance"])406            if model_config.get("sensitive_word_avoidance")407            else None408        )409        self.external_data_tools = (410            json.dumps(model_config["external_data_tools"]) if model_config.get("external_data_tools") else None411        )412        self.model = json.dumps(model_config["model"]) if model_config.get("model") else None413        self.user_input_form = (414            json.dumps(model_config["user_input_form"]) if model_config.get("user_input_form") else None415        )416        self.dataset_query_variable = model_config.get("dataset_query_variable")417        self.pre_prompt = model_config["pre_prompt"]418        self.agent_mode = json.dumps(model_config["agent_mode"]) if model_config.get("agent_mode") else None419        self.retriever_resource = (420            json.dumps(model_config["retriever_resource"]) if model_config.get("retriever_resource") else None421        )422        self.prompt_type = model_config.get("prompt_type", "simple")423        self.chat_prompt_config = (424            json.dumps(model_config.get("chat_prompt_config")) if model_config.get("chat_prompt_config") else None425        )426        self.completion_prompt_config = (427            json.dumps(model_config.get("completion_prompt_config"))428            if model_config.get("completion_prompt_config")429            else None430        )431        self.dataset_configs = (432            json.dumps(model_config.get("dataset_configs")) if model_config.get("dataset_configs") else None433        )434        self.file_upload = json.dumps(model_config.get("file_upload")) if model_config.get("file_upload") else None435        return self436 437    def copy(self):438        new_app_model_config = AppModelConfig(439            id=self.id,440            app_id=self.app_id,441            opening_statement=self.opening_statement,442            suggested_questions=self.suggested_questions,443            suggested_questions_after_answer=self.suggested_questions_after_answer,444            speech_to_text=self.speech_to_text,445            text_to_speech=self.text_to_speech,446            more_like_this=self.more_like_this,447            sensitive_word_avoidance=self.sensitive_word_avoidance,448            external_data_tools=self.external_data_tools,449            model=self.model,450            user_input_form=self.user_input_form,451            dataset_query_variable=self.dataset_query_variable,452            pre_prompt=self.pre_prompt,453            agent_mode=self.agent_mode,454            retriever_resource=self.retriever_resource,455            prompt_type=self.prompt_type,456            chat_prompt_config=self.chat_prompt_config,457            completion_prompt_config=self.completion_prompt_config,458            dataset_configs=self.dataset_configs,459            file_upload=self.file_upload,460        )461 462        return new_app_model_config463 464 465class RecommendedApp(db.Model):466    __tablename__ = "recommended_apps"467    __table_args__ = (468        db.PrimaryKeyConstraint("id", name="recommended_app_pkey"),469        db.Index("recommended_app_app_id_idx", "app_id"),470        db.Index("recommended_app_is_listed_idx", "is_listed", "language"),471    )472 473    id = db.Column(StringUUID, primary_key=True, server_default=db.text("uuid_generate_v4()"))474    app_id = db.Column(StringUUID, nullable=False)475    description = db.Column(db.JSON, nullable=False)476    copyright = db.Column(db.String(255), nullable=False)477    privacy_policy = db.Column(db.String(255), nullable=False)478    custom_disclaimer: Mapped[str] = mapped_column(sa.TEXT, default="")479    category = db.Column(db.String(255), nullable=False)480    position = db.Column(db.Integer, nullable=False, default=0)481    is_listed = db.Column(db.Boolean, nullable=False, default=True)482    install_count = db.Column(db.Integer, nullable=False, default=0)483    language = db.Column(db.String(255), nullable=False, server_default=db.text("'en-US'::character varying"))484    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))485    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))486 487    @property488    def app(self):489        app = db.session.query(App).filter(App.id == self.app_id).first()490        return app491 492 493class InstalledApp(db.Model):494    __tablename__ = "installed_apps"495    __table_args__ = (496        db.PrimaryKeyConstraint("id", name="installed_app_pkey"),497        db.Index("installed_app_tenant_id_idx", "tenant_id"),498        db.Index("installed_app_app_id_idx", "app_id"),499        db.UniqueConstraint("tenant_id", "app_id", name="unique_tenant_app"),500    )501 502    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))503    tenant_id = db.Column(StringUUID, nullable=False)504    app_id = db.Column(StringUUID, nullable=False)505    app_owner_tenant_id = db.Column(StringUUID, nullable=False)506    position = db.Column(db.Integer, nullable=False, default=0)507    is_pinned = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))508    last_used_at = db.Column(db.DateTime, nullable=True)509    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))510 511    @property512    def app(self):513        app = db.session.query(App).filter(App.id == self.app_id).first()514        return app515 516    @property517    def tenant(self):518        tenant = db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first()519        return tenant520 521 522class Conversation(db.Model):523    __tablename__ = "conversations"524    __table_args__ = (525        db.PrimaryKeyConstraint("id", name="conversation_pkey"),526        db.Index("conversation_app_from_user_idx", "app_id", "from_source", "from_end_user_id"),527    )528 529    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))530    app_id = db.Column(StringUUID, nullable=False)531    app_model_config_id = db.Column(StringUUID, nullable=True)532    model_provider = db.Column(db.String(255), nullable=True)533    override_model_configs = db.Column(db.Text)534    model_id = db.Column(db.String(255), nullable=True)535    mode = db.Column(db.String(255), nullable=False)536    name = db.Column(db.String(255), nullable=False)537    summary = db.Column(db.Text)538    _inputs: Mapped[dict] = mapped_column("inputs", db.JSON)539    introduction = db.Column(db.Text)540    system_instruction = db.Column(db.Text)541    system_instruction_tokens = db.Column(db.Integer, nullable=False, server_default=db.text("0"))542    status = db.Column(db.String(255), nullable=False)543    invoke_from = db.Column(db.String(255), nullable=True)544    from_source = db.Column(db.String(255), nullable=False)545    from_end_user_id = db.Column(StringUUID)546    from_account_id = db.Column(StringUUID)547    read_at = db.Column(db.DateTime)548    read_account_id = db.Column(StringUUID)549    dialogue_count: Mapped[int] = mapped_column(default=0)550    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))551    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))552 553    messages = db.relationship("Message", backref="conversation", lazy="select", passive_deletes="all")554    message_annotations = db.relationship(555        "MessageAnnotation", backref="conversation", lazy="select", passive_deletes="all"556    )557 558    is_deleted = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))559 560    @property561    def inputs(self):562        inputs = self._inputs.copy()563        for key, value in inputs.items():564            if isinstance(value, dict) and value.get("dify_model_identity") == FILE_MODEL_IDENTITY:565                inputs[key] = File.model_validate(value)566            elif isinstance(value, list) and all(567                isinstance(item, dict) and item.get("dify_model_identity") == FILE_MODEL_IDENTITY for item in value568            ):569                inputs[key] = [File.model_validate(item) for item in value]570        return inputs571 572    @inputs.setter573    def inputs(self, value: Mapping[str, Any]):574        inputs = dict(value)575        for k, v in inputs.items():576            if isinstance(v, File):577                inputs[k] = v.model_dump()578            elif isinstance(v, list) and all(isinstance(item, File) for item in v):579                inputs[k] = [item.model_dump() for item in v]580        self._inputs = inputs581 582    @property583    def model_config(self):584        model_config = {}585        if self.mode == AppMode.ADVANCED_CHAT.value:586            if self.override_model_configs:587                override_model_configs = json.loads(self.override_model_configs)588                model_config = override_model_configs589        else:590            if self.override_model_configs:591                override_model_configs = json.loads(self.override_model_configs)592 593                if "model" in override_model_configs:594                    app_model_config = AppModelConfig()595                    app_model_config = app_model_config.from_model_config_dict(override_model_configs)596                    model_config = app_model_config.to_dict()597                else:598                    model_config["configs"] = override_model_configs599            else:600                app_model_config = (601                    db.session.query(AppModelConfig).filter(AppModelConfig.id == self.app_model_config_id).first()602                )603 604                model_config = app_model_config.to_dict()605 606        model_config["model_id"] = self.model_id607        model_config["provider"] = self.model_provider608 609        return model_config610 611    @property612    def summary_or_query(self):613        if self.summary:614            return self.summary615        else:616            first_message = self.first_message617            if first_message:618                return first_message.query619            else:620                return ""621 622    @property623    def annotated(self):624        return db.session.query(MessageAnnotation).filter(MessageAnnotation.conversation_id == self.id).count() > 0625 626    @property627    def annotation(self):628        return db.session.query(MessageAnnotation).filter(MessageAnnotation.conversation_id == self.id).first()629 630    @property631    def message_count(self):632        return db.session.query(Message).filter(Message.conversation_id == self.id).count()633 634    @property635    def user_feedback_stats(self):636        like = (637            db.session.query(MessageFeedback)638            .filter(639                MessageFeedback.conversation_id == self.id,640                MessageFeedback.from_source == "user",641                MessageFeedback.rating == "like",642            )643            .count()644        )645 646        dislike = (647            db.session.query(MessageFeedback)648            .filter(649                MessageFeedback.conversation_id == self.id,650                MessageFeedback.from_source == "user",651                MessageFeedback.rating == "dislike",652            )653            .count()654        )655 656        return {"like": like, "dislike": dislike}657 658    @property659    def admin_feedback_stats(self):660        like = (661            db.session.query(MessageFeedback)662            .filter(663                MessageFeedback.conversation_id == self.id,664                MessageFeedback.from_source == "admin",665                MessageFeedback.rating == "like",666            )667            .count()668        )669 670        dislike = (671            db.session.query(MessageFeedback)672            .filter(673                MessageFeedback.conversation_id == self.id,674                MessageFeedback.from_source == "admin",675                MessageFeedback.rating == "dislike",676            )677            .count()678        )679 680        return {"like": like, "dislike": dislike}681 682    @property683    def first_message(self):684        return db.session.query(Message).filter(Message.conversation_id == self.id).first()685 686    @property687    def app(self):688        return db.session.query(App).filter(App.id == self.app_id).first()689 690    @property691    def from_end_user_session_id(self):692        if self.from_end_user_id:693            end_user = db.session.query(EndUser).filter(EndUser.id == self.from_end_user_id).first()694            if end_user:695                return end_user.session_id696 697        return None698 699    @property700    def from_account_name(self):701        if self.from_account_id:702            account = db.session.query(Account).filter(Account.id == self.from_account_id).first()703            if account:704                return account.name705 706        return None707 708    @property709    def in_debug_mode(self):710        return self.override_model_configs is not None711 712 713class Message(db.Model):714    __tablename__ = "messages"715    __table_args__ = (716        db.PrimaryKeyConstraint("id", name="message_pkey"),717        db.Index("message_app_id_idx", "app_id", "created_at"),718        db.Index("message_conversation_id_idx", "conversation_id"),719        db.Index("message_end_user_idx", "app_id", "from_source", "from_end_user_id"),720        db.Index("message_account_idx", "app_id", "from_source", "from_account_id"),721        db.Index("message_workflow_run_id_idx", "conversation_id", "workflow_run_id"),722    )723 724    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))725    app_id = db.Column(StringUUID, nullable=False)726    model_provider = db.Column(db.String(255), nullable=True)727    model_id = db.Column(db.String(255), nullable=True)728    override_model_configs = db.Column(db.Text)729    conversation_id = db.Column(StringUUID, db.ForeignKey("conversations.id"), nullable=False)730    _inputs: Mapped[dict] = mapped_column("inputs", db.JSON)731    query: Mapped[str] = db.Column(db.Text, nullable=False)732    message = db.Column(db.JSON, nullable=False)733    message_tokens = db.Column(db.Integer, nullable=False, server_default=db.text("0"))734    message_unit_price = db.Column(db.Numeric(10, 4), nullable=False)735    message_price_unit = db.Column(db.Numeric(10, 7), nullable=False, server_default=db.text("0.001"))736    answer: Mapped[str] = db.Column(db.Text, nullable=False)737    answer_tokens = db.Column(db.Integer, nullable=False, server_default=db.text("0"))738    answer_unit_price = db.Column(db.Numeric(10, 4), nullable=False)739    answer_price_unit = db.Column(db.Numeric(10, 7), nullable=False, server_default=db.text("0.001"))740    parent_message_id = db.Column(StringUUID, nullable=True)741    provider_response_latency = db.Column(db.Float, nullable=False, server_default=db.text("0"))742    total_price = db.Column(db.Numeric(10, 7))743    currency = db.Column(db.String(255), nullable=False)744    status = db.Column(db.String(255), nullable=False, server_default=db.text("'normal'::character varying"))745    error = db.Column(db.Text)746    message_metadata = db.Column(db.Text)747    invoke_from: Mapped[Optional[str]] = db.Column(db.String(255), nullable=True)748    from_source = db.Column(db.String(255), nullable=False)749    from_end_user_id: Mapped[Optional[str]] = db.Column(StringUUID)750    from_account_id: Mapped[Optional[str]] = db.Column(StringUUID)751    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))752    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))753    agent_based = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))754    workflow_run_id = db.Column(StringUUID)755 756    @property757    def inputs(self):758        inputs = self._inputs.copy()759        for key, value in inputs.items():760            if isinstance(value, dict) and value.get("dify_model_identity") == FILE_MODEL_IDENTITY:761                inputs[key] = File.model_validate(value)762            elif isinstance(value, list) and all(763                isinstance(item, dict) and item.get("dify_model_identity") == FILE_MODEL_IDENTITY for item in value764            ):765                inputs[key] = [File.model_validate(item) for item in value]766        return inputs767 768    @inputs.setter769    def inputs(self, value: Mapping[str, Any]):770        inputs = dict(value)771        for k, v in inputs.items():772            if isinstance(v, File):773                inputs[k] = v.model_dump()774            elif isinstance(v, list) and all(isinstance(item, File) for item in v):775                inputs[k] = [item.model_dump() for item in v]776        self._inputs = inputs777 778    @property779    def re_sign_file_url_answer(self) -> str:780        if not self.answer:781            return self.answer782 783        pattern = r"\[!?.*?\]\((((http|https):\/\/.+)?\/files\/(tools\/)?[\w-]+.*?timestamp=.*&nonce=.*&sign=.*)\)"784        matches = re.findall(pattern, self.answer)785 786        if not matches:787            return self.answer788 789        urls = [match[0] for match in matches]790 791        # remove duplicate urls792        urls = list(set(urls))793 794        if not urls:795            return self.answer796 797        re_sign_file_url_answer = self.answer798        for url in urls:799            if "files/tools" in url:800                # get tool file id801                tool_file_id_pattern = r"\/files\/tools\/([\.\w-]+)?\?timestamp="802                result = re.search(tool_file_id_pattern, url)803                if not result:804                    continue805 806                tool_file_id = result.group(1)807 808                # get extension809                if "." in tool_file_id:810                    split_result = tool_file_id.split(".")811                    extension = f".{split_result[-1]}"812                    if len(extension) > 10:813                        extension = ".bin"814                    tool_file_id = split_result[0]815                else:816                    extension = ".bin"817 818                if not tool_file_id:819                    continue820 821                sign_url = ToolFileParser.get_tool_file_manager().sign_file(822                    tool_file_id=tool_file_id, extension=extension823                )824            elif "file-preview" in url:825                # get upload file id826                upload_file_id_pattern = r"\/files\/([\w-]+)\/file-preview?\?timestamp="827                result = re.search(upload_file_id_pattern, url)828                if not result:829                    continue830 831                upload_file_id = result.group(1)832                if not upload_file_id:833                    continue834                sign_url = file_helpers.get_signed_file_url(upload_file_id)835            elif "image-preview" in url:836                # image-preview is deprecated, use file-preview instead837                upload_file_id_pattern = r"\/files\/([\w-]+)\/image-preview?\?timestamp="838                result = re.search(upload_file_id_pattern, url)839                if not result:840                    continue841                upload_file_id = result.group(1)842                if not upload_file_id:843                    continue844                sign_url = file_helpers.get_signed_file_url(upload_file_id)845            else:846                continue847 848            re_sign_file_url_answer = re_sign_file_url_answer.replace(url, sign_url)849 850        return re_sign_file_url_answer851 852    @property853    def user_feedback(self):854        feedback = (855            db.session.query(MessageFeedback)856            .filter(MessageFeedback.message_id == self.id, MessageFeedback.from_source == "user")857            .first()858        )859        return feedback860 861    @property862    def admin_feedback(self):863        feedback = (864            db.session.query(MessageFeedback)865            .filter(MessageFeedback.message_id == self.id, MessageFeedback.from_source == "admin")866            .first()867        )868        return feedback869 870    @property871    def feedbacks(self):872        feedbacks = db.session.query(MessageFeedback).filter(MessageFeedback.message_id == self.id).all()873        return feedbacks874 875    @property876    def annotation(self):877        annotation = db.session.query(MessageAnnotation).filter(MessageAnnotation.message_id == self.id).first()878        return annotation879 880    @property881    def annotation_hit_history(self):882        annotation_history = (883            db.session.query(AppAnnotationHitHistory).filter(AppAnnotationHitHistory.message_id == self.id).first()884        )885        if annotation_history:886            annotation = (887                db.session.query(MessageAnnotation)888                .filter(MessageAnnotation.id == annotation_history.annotation_id)889                .first()890            )891            return annotation892        return None893 894    @property895    def app_model_config(self):896        conversation = db.session.query(Conversation).filter(Conversation.id == self.conversation_id).first()897        if conversation:898            return (899                db.session.query(AppModelConfig).filter(AppModelConfig.id == conversation.app_model_config_id).first()900            )901 902        return None903 904    @property905    def in_debug_mode(self):906        return self.override_model_configs is not None907 908    @property909    def message_metadata_dict(self) -> dict:910        return json.loads(self.message_metadata) if self.message_metadata else {}911 912    @property913    def agent_thoughts(self):914        return (915            db.session.query(MessageAgentThought)916            .filter(MessageAgentThought.message_id == self.id)917            .order_by(MessageAgentThought.position.asc())918            .all()919        )920 921    @property922    def retriever_resources(self):923        return (924            db.session.query(DatasetRetrieverResource)925            .filter(DatasetRetrieverResource.message_id == self.id)926            .order_by(DatasetRetrieverResource.position.asc())927            .all()928        )929 930    @property931    def message_files(self):932        from factories import file_factory933 934        message_files = db.session.query(MessageFile).filter(MessageFile.message_id == self.id).all()935        current_app = db.session.query(App).filter(App.id == self.app_id).first()936        if not current_app:937            raise ValueError(f"App {self.app_id} not found")938 939        files: list[File] = []940        for message_file in message_files:941            if message_file.transfer_method == "local_file":942                if message_file.upload_file_id is None:943                    raise ValueError(f"MessageFile {message_file.id} is a local file but has no upload_file_id")944                file = file_factory.build_from_mapping(945                    mapping={946                        "id": message_file.id,947                        "upload_file_id": message_file.upload_file_id,948                        "transfer_method": message_file.transfer_method,949                        "type": message_file.type,950                    },951                    tenant_id=current_app.tenant_id,952                    user_id=self.from_account_id or self.from_end_user_id or "",953                    role=CreatedByRole(message_file.created_by_role),954                    config=FileExtraConfig(),955                )956            elif message_file.transfer_method == "remote_url":957                if message_file.url is None:958                    raise ValueError(f"MessageFile {message_file.id} is a remote url but has no url")959                file = file_factory.build_from_mapping(960                    mapping={961                        "id": message_file.id,962                        "type": message_file.type,963                        "transfer_method": message_file.transfer_method,964                        "url": message_file.url,965                    },966                    tenant_id=current_app.tenant_id,967                    user_id=self.from_account_id or self.from_end_user_id or "",968                    role=CreatedByRole(message_file.created_by_role),969                    config=FileExtraConfig(),970                )971            elif message_file.transfer_method == "tool_file":972                if message_file.upload_file_id is None:973                    assert message_file.url is not None974                    message_file.upload_file_id = message_file.url.split("/")[-1].split(".")[0]975                mapping = {976                    "id": message_file.id,977                    "type": message_file.type,978                    "transfer_method": message_file.transfer_method,979                    "tool_file_id": message_file.upload_file_id,980                }981                file = file_factory.build_from_mapping(982                    mapping=mapping,983                    tenant_id=current_app.tenant_id,984                    user_id=self.from_account_id or self.from_end_user_id or "",985                    role=CreatedByRole(message_file.created_by_role),986                    config=FileExtraConfig(),987                )988            else:989                raise ValueError(990                    f"MessageFile {message_file.id} has an invalid transfer_method {message_file.transfer_method}"991                )992            files.append(file)993 994        result = [995            {"belongs_to": message_file.belongs_to, **file.to_dict()}996            for (file, message_file) in zip(files, message_files)997        ]998 999        db.session.commit()1000        return result1001 1002    @property1003    def workflow_run(self):1004        if self.workflow_run_id:1005            from .workflow import WorkflowRun1006 1007            return db.session.query(WorkflowRun).filter(WorkflowRun.id == self.workflow_run_id).first()1008 1009        return None1010 1011    def to_dict(self) -> dict:1012        return {1013            "id": self.id,1014            "app_id": self.app_id,1015            "conversation_id": self.conversation_id,1016            "inputs": self.inputs,1017            "query": self.query,1018            "message": self.message,1019            "answer": self.answer,1020            "status": self.status,1021            "error": self.error,1022            "message_metadata": self.message_metadata_dict,1023            "from_source": self.from_source,1024            "from_end_user_id": self.from_end_user_id,1025            "from_account_id": self.from_account_id,1026            "created_at": self.created_at.isoformat(),1027            "updated_at": self.updated_at.isoformat(),1028            "agent_based": self.agent_based,1029            "workflow_run_id": self.workflow_run_id,1030        }1031 1032    @classmethod1033    def from_dict(cls, data: dict):1034        return cls(1035            id=data["id"],1036            app_id=data["app_id"],1037            conversation_id=data["conversation_id"],1038            inputs=data["inputs"],1039            query=data["query"],1040            message=data["message"],1041            answer=data["answer"],1042            status=data["status"],1043            error=data["error"],1044            message_metadata=json.dumps(data["message_metadata"]),1045            from_source=data["from_source"],1046            from_end_user_id=data["from_end_user_id"],1047            from_account_id=data["from_account_id"],1048            created_at=data["created_at"],1049            updated_at=data["updated_at"],1050            agent_based=data["agent_based"],1051            workflow_run_id=data["workflow_run_id"],1052        )1053 1054 1055class MessageFeedback(db.Model):1056    __tablename__ = "message_feedbacks"1057    __table_args__ = (1058        db.PrimaryKeyConstraint("id", name="message_feedback_pkey"),1059        db.Index("message_feedback_app_idx", "app_id"),1060        db.Index("message_feedback_message_idx", "message_id", "from_source"),1061        db.Index("message_feedback_conversation_idx", "conversation_id", "from_source", "rating"),1062    )1063 1064    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))1065    app_id = db.Column(StringUUID, nullable=False)1066    conversation_id = db.Column(StringUUID, nullable=False)1067    message_id = db.Column(StringUUID, nullable=False)1068    rating = db.Column(db.String(255), nullable=False)1069    content = db.Column(db.Text)1070    from_source = db.Column(db.String(255), nullable=False)1071    from_end_user_id = db.Column(StringUUID)1072    from_account_id = db.Column(StringUUID)1073    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))1074    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))1075 1076    @property1077    def from_account(self):1078        account = db.session.query(Account).filter(Account.id == self.from_account_id).first()1079        return account1080 1081 1082class MessageFile(db.Model):1083    __tablename__ = "message_files"1084    __table_args__ = (1085        db.PrimaryKeyConstraint("id", name="message_file_pkey"),1086        db.Index("message_file_message_idx", "message_id"),1087        db.Index("message_file_created_by_idx", "created_by"),1088    )1089 1090    def __init__(1091        self,1092        *,1093        message_id: str,1094        type: FileType,1095        transfer_method: FileTransferMethod,1096        url: str | None = None,1097        belongs_to: Literal["user", "assistant"] | None = None,1098        upload_file_id: str | None = None,1099        created_by_role: CreatedByRole,1100        created_by: str,1101    ):1102        self.message_id = message_id1103        self.type = type1104        self.transfer_method = transfer_method1105        self.url = url1106        self.belongs_to = belongs_to1107        self.upload_file_id = upload_file_id1108        self.created_by_role = created_by_role.value1109        self.created_by = created_by1110 1111    id: Mapped[str] = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))1112    message_id: Mapped[str] = db.Column(StringUUID, nullable=False)1113    type: Mapped[str] = db.Column(db.String(255), nullable=False)1114    transfer_method: Mapped[str] = db.Column(db.String(255), nullable=False)1115    url: Mapped[Optional[str]] = db.Column(db.Text, nullable=True)1116    belongs_to: Mapped[Optional[str]] = db.Column(db.String(255), nullable=True)1117    upload_file_id: Mapped[Optional[str]] = db.Column(StringUUID, nullable=True)1118    created_by_role: Mapped[str] = db.Column(db.String(255), nullable=False)1119    created_by: Mapped[str] = db.Column(StringUUID, nullable=False)1120    created_at: Mapped[datetime] = db.Column(1121        db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")1122    )1123 1124 1125class MessageAnnotation(db.Model):1126    __tablename__ = "message_annotations"1127    __table_args__ = (1128        db.PrimaryKeyConstraint("id", name="message_annotation_pkey"),1129        db.Index("message_annotation_app_idx", "app_id"),1130        db.Index("message_annotation_conversation_idx", "conversation_id"),1131        db.Index("message_annotation_message_idx", "message_id"),1132    )1133 1134    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))1135    app_id = db.Column(StringUUID, nullable=False)1136    conversation_id = db.Column(StringUUID, db.ForeignKey("conversations.id"), nullable=True)1137    message_id = db.Column(StringUUID, nullable=True)1138    question = db.Column(db.Text, nullable=True)1139    content = db.Column(db.Text, nullable=False)1140    hit_count = db.Column(db.Integer, nullable=False, server_default=db.text("0"))1141    account_id = db.Column(StringUUID, nullable=False)1142    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))1143    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))1144 1145    @property1146    def account(self):1147        account = db.session.query(Account).filter(Account.id == self.account_id).first()1148        return account1149 1150    @property1151    def annotation_create_account(self):1152        account = db.session.query(Account).filter(Account.id == self.account_id).first()1153        return account1154 1155 1156class AppAnnotationHitHistory(db.Model):1157    __tablename__ = "app_annotation_hit_histories"1158    __table_args__ = (1159        db.PrimaryKeyConstraint("id", name="app_annotation_hit_histories_pkey"),1160        db.Index("app_annotation_hit_histories_app_idx", "app_id"),1161        db.Index("app_annotation_hit_histories_account_idx", "account_id"),1162        db.Index("app_annotation_hit_histories_annotation_idx", "annotation_id"),1163        db.Index("app_annotation_hit_histories_message_idx", "message_id"),1164    )1165 1166    id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))1167    app_id = db.Column(StringUUID, nullable=False)1168    annotation_id = db.Column(StringUUID, nullable=False)1169    source = db.Column(db.Text, nullable=False)1170    question = db.Column(db.Text, nullable=False)1171    account_id = db.Column(StringUUID, nullable=False)1172    created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))1173    score = db.Column(Float, nullable=False, server_default=db.text("0"))1174    message_id = db.Column(StringUUID, nullable=False)1175    annotation_question = db.Column(db.Text, nullable=False)1176    annotation_content = db.Column(db.Text, nullable=False)1177 1178    @property1179    def account(self):1180        account = (1181            db.session.query(Account)1182            .join(MessageAnnotation, MessageAnnotation.account_id == Account.id)1183            .filter(MessageAnnotation.id == self.annotation_id)1184            .first()1185        )1186        return account1187 1188    @property1189    def annotation_create_account(self):1190        account = db.session.query(Account).filter(Account.id == self.account_id).first()1191        return account1192 1193 1194class AppAnnotationSetting(db.Model):1195    __tablename__ = "app_annotation_settings"1196    __table_args__ = (1197        db.PrimaryKeyConstraint("id", name="app_annotation_settings_pkey"),1198        db.Index("app_annotation_settings_app_idx", "app_id"),1199    )1200 

Showing the first 1,200 of 1665 lines. Download the file for the rest.