Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: The demonstration page supports modifying dialogue summaries #2348

Merged
merged 26 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
752e1eb
feat: The demonstration page supports modifying dialogue summaries
shaohuzhang1 Feb 21, 2025
b50db41
feat: History record supports modifying chat abstract
wangdan-fit2cloud Feb 21, 2025
cd03e6b
Variable assign (#2346)
liuruibin Feb 21, 2025
b1a3d97
feat: User input parameters and interface parameters support adjustin…
wangdan-fit2cloud Feb 21, 2025
43e2e5e
fix: type bugs
wangdan-fit2cloud Feb 21, 2025
3d7a9fc
fix: Function library execution role group (#2358)
shaohuzhang1 Feb 24, 2025
fbca792
fix: drawer title for slack setting
guqing Feb 24, 2025
f034f2a
chore: Update README.md (#2363)
maninhill Feb 24, 2025
097c5f8
chore: Update README.md (#2364)
maninhill Feb 24, 2025
83254bf
chore: Update README.md (#2365)
maninhill Feb 24, 2025
3518433
chore: Update README.md (#2366)
maninhill Feb 24, 2025
8b293ae
chore(i18n): optimize english translations and standardize title case…
guqing Feb 24, 2025
b6de93e
chore: Update README.md (#2369)
maninhill Feb 24, 2025
131b186
chore: Update README.md (#2371)
maninhill Feb 24, 2025
1a4cc6e
fix: Workflow execution decimal cannot be serialized (#2372)
shaohuzhang1 Feb 24, 2025
99b8448
fix: Remove vllm image cache
liuruibin Feb 24, 2025
5869af0
docs: Create an application and add workflow parameters (#2374)
shaohuzhang1 Feb 24, 2025
a6525d3
fix: VLLM supplier recalculates token function (#2375)
shaohuzhang1 Feb 24, 2025
28c2d6b
feat: Create workflow applications that support template selection
wangdan-fit2cloud Feb 24, 2025
ff99830
chore(i18n): optimize english translations for appearance settings
guqing Feb 24, 2025
16cfca6
fix: Fix browser text to speech cannot resume and cancel
liuruibin Feb 24, 2025
efd6de3
perf: Add a tip before exiting the workflow
wangdan-fit2cloud Feb 24, 2025
22fb799
feat: Upgrade xinference client (#2381)
shaohuzhang1 Feb 24, 2025
1368d37
refactor: ollama support rerank
wxg0103 Feb 24, 2025
3e347d2
fix: The locally loaded vector model is missing the libGL.so library …
shaohuzhang1 Feb 24, 2025
3361acf
feat: History record supports modifying chat abstract
wangdan-fit2cloud Feb 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,69 @@ Access MaxKB web interface at `http://your_server_ip:8080` with default admin cr
- LLM Framework:[LangChain](https://www.langchain.com/)
- Database:[PostgreSQL + pgvector](https://www.postgresql.org/)

## Feature Comparison

MaxKB is positioned as an Ready-to-use RAG (Retrieval-Augmented Generation) intelligent Q&A application, rather than a middleware platform for building large model applications. The following table is merely a comparison from a functional perspective.

<table style="width: 100%;">
<tr>
<th align="center">Feature</th>
<th align="center">LangChain</th>
<th align="center">Dify.AI</th>
<th align="center">Flowise</th>
<th align="center">MaxKB <br>(Built upon LangChain)</th>
</tr>
<tr>
<td align="center">Supported LLMs</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
</tr>
<tr>
<td align="center">RAG Engine</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">Agent</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">❌</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">Workflow</td>
<td align="center">❌</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">Observability</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">❌</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">SSO/Access control</td>
<td align="center">❌</td>
<td align="center">✅</td>
<td align="center">❌</td>
<td align="center">✅ (Pro)</td>
</tr>
<tr>
<td align="center">On-premise Deployment</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
</table>

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/MaxKB&type=Date)](https://star-history.com/#1Panel-dev/MaxKB&Date)
Expand Down
7 changes: 5 additions & 2 deletions apps/application/flow/step_node/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@
from .speech_to_text_step_node import BaseSpeechToTextNode
from .start_node import *
from .text_to_speech_step_node.impl.base_text_to_speech_node import BaseTextToSpeechNode
from .variable_assign_node import BaseVariableAssignNode

node_list = [BaseStartStepNode, BaseChatNode, BaseSearchDatasetNode, BaseQuestionNode, BaseConditionNode, BaseReplyNode,
node_list = [BaseStartStepNode, BaseChatNode, BaseSearchDatasetNode, BaseQuestionNode,
BaseConditionNode, BaseReplyNode,
BaseFunctionNodeNode, BaseFunctionLibNodeNode, BaseRerankerNode, BaseApplicationNode,
BaseDocumentExtractNode,
BaseImageUnderstandNode, BaseFormNode, BaseSpeechToTextNode, BaseTextToSpeechNode,BaseImageGenerateNode]
BaseImageUnderstandNode, BaseFormNode, BaseSpeechToTextNode, BaseTextToSpeechNode,
BaseImageGenerateNode, BaseVariableAssignNode]


def get_node(node_type):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# coding=utf-8

from .impl import *
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# coding=utf-8

from typing import Type

from django.utils.translation import gettext_lazy as _
from rest_framework import serializers

from application.flow.i_step_node import INode, NodeResult
from common.util.field_message import ErrMessage


class VariableAssignNodeParamsSerializer(serializers.Serializer):
variable_list = serializers.ListField(required=True,
error_messages=ErrMessage.list(_("Reference Field")))


class IVariableAssignNode(INode):
type = 'variable-assign-node'

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return VariableAssignNodeParamsSerializer

def _run(self):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)

def execute(self, variable_list, stream, **kwargs) -> NodeResult:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# coding=utf-8
"""
@project: maxkb
@Author:虎
@file: __init__.py
@date:2024/6/11 17:49
@desc:
"""
from .base_variable_assign_node import *
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# coding=utf-8
import json
from typing import List

from application.flow.i_step_node import NodeResult
from application.flow.step_node.variable_assign_node.i_variable_assign_node import IVariableAssignNode


class BaseVariableAssignNode(IVariableAssignNode):
def save_context(self, details, workflow_manage):
self.context['variable_list'] = details.get('variable_list')
self.context['result_list'] = details.get('result_list')

def execute(self, variable_list, stream, **kwargs) -> NodeResult:
#
result_list = []
for variable in variable_list:
if 'fields' not in variable:
continue
if 'global' == variable['fields'][0]:
result = {
'name': variable['name'],
'input_value': self.get_reference_content(variable['fields']),
}
if variable['source'] == 'custom':
if variable['type'] in ['dict', 'array']:
if isinstance(variable['value'], dict) or isinstance(variable['value'], list):
val = variable['value']
else:
val = json.loads(variable['value'])
self.workflow_manage.context[variable['fields'][1]] = val
result['output_value'] = variable['value'] = val
else:
self.workflow_manage.context[variable['fields'][1]] = variable['value']
result['output_value'] = variable['value']
else:
reference = self.get_reference_content(variable['reference'])
self.workflow_manage.context[variable['fields'][1]] = reference
result['output_value'] = reference
result_list.append(result)

return NodeResult({'variable_list': variable_list, 'result_list': result_list}, {})

def get_reference_content(self, fields: List[str]):
return str(self.workflow_manage.get_reference_field(
fields[0],
fields[1:]))

def get_details(self, index: int, **kwargs):
return {
'name': self.node.properties.get('stepName'),
"index": index,
'run_time': self.context.get('run_time'),
'type': self.node.type,
'variable_list': self.context.get('variable_list'),
'result_list': self.context.get('result_list'),
'status': self.status,
'err_message': self.err_message
}
3 changes: 3 additions & 0 deletions apps/application/models/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@desc:
"""
import datetime
import decimal
import json
import uuid

Expand Down Expand Up @@ -140,6 +141,8 @@ def default(self, obj):
return str(obj)
if isinstance(obj, datetime.datetime):
return obj.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(obj, decimal.Decimal):
return float(obj)
else:
return json.JSONEncoder.default(self, obj)

Expand Down
13 changes: 13 additions & 0 deletions apps/application/serializers/chat_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def valid_model_params_setting(model_id, model_params_setting):
credential.get_model_params_setting_form(model.model_name).valid_form(model_params_setting)


class ReAbstractInstanceSerializers(serializers.Serializer):
abstract = serializers.CharField(required=True, error_messages=ErrMessage.char(_("abstract")))


class ChatSerializers(serializers.Serializer):
class Operate(serializers.Serializer):
chat_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid(_("Conversation ID")))
Expand All @@ -78,6 +82,15 @@ def logic_delete(self, with_valid=True):
is_deleted=True)
return True

def re_abstract(self, instance, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
ReAbstractInstanceSerializers(data=instance).is_valid(raise_exception=True)

QuerySet(Chat).filter(id=self.data.get('chat_id'), application_id=self.data.get('application_id')).update(
abstract=instance.get('abstract'))
return True

def delete(self, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some recommendations for optimizing and improving the code:

  1. Class Naming Conventions: Consider using PascalCase or CamelCase for class names to make them more readable.

  2. Serializer Structure Simplification: The ChatSerializers has a nested serializer in its Operate class which adds unnecessary complexity. It might be better to have separate classes for different operations instead of nesting serializers.

  3. Error Handling Cleanup: Use consistent error handling throughout. Currently, it's okay but ensure that all exceptions raise the same type of exception.

  4. Optimization in Logic Delete Method: The method can be optimized by directly accessing the database without filtering first, then updating based on conditions.

  5. ReAbstract Instance Method:

    • Avoid calling .get() inside the loop if you're using Python 3.7+, as dictionaries maintain insertion order.
      Add a check before attempting to update the abstract field to ensure there is an abstract value to overwrite.
  6. General Formatting: Ensure consistent formatting including spacing between statements, variable definitions, etc., this improves readability.

Example refactored version:

def valid_model_params_setting(model_id, model_params_setting):
    credential.get_model_params_setting_form(model.model_name).valid_form(model_params_setting)


class ChatSerializers(serializers.Serializer):
    class ReAbstractInstanceSerializer(serializers.Serializer):
        abstract = serializers.CharField(
            required=True,
            error_messages=ErrMessage.char(_("abstract")),
        )

    class Operate(serializers.Serializer):
        chat_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid(_("Conversation ID")))

        def logic_delete(self, with_valid=True):
            with_valid and self.is_valid(raise_exception=True)
            with_valid and QuerySet(Chat).filter(id=self.data['chat_id'], application_id=self.data['application_id']) \
                .update(is_deleted=True)
            return True

        def re_abstract(self, instance, with_valid=True):
            if with_valid:
                self.is_valid(raise_exception=True)
                ReAbstractInstanceSerializer(instance).is_valid(raise_exception=True)

            current_chat = query_set.filter(id=self.data['chat_id'], application_id=self.data['application_id']).first()
            if not current_chat or current_chat.abstract == '':  # Check if an abstract exists to avoid overwriting
                return False

            new_data = instance.copy()  # Deep copy if necessary to preserve old values if needed
            new_data['abstract'] = ''
            QuerySet(Chat).filter(id=self.data['chat_id'], application_id=self.data['application_id']).update(**new_data)
            return True

        def delete(self, with_valid=True):
            with_valid and self.is_valid(raise_exception=True)

This version consolidates similar functionalities under separate methods and ensures cleaner, more efficient execution of each operation.

Expand Down
10 changes: 6 additions & 4 deletions apps/application/swagger_api/application_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def get_request_body_api():
default=[]),
'edges': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_OBJECT),
title=_('Connection List'), description=_("Connection List"),
default={}),
default=[]),

}
)
Expand Down Expand Up @@ -324,7 +324,8 @@ def get_request_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['name', 'desc', 'model_id', 'dialogue_number', 'dataset_setting', 'model_setting',
'problem_optimization', 'stt_model_enable', 'stt_model_enable', 'tts_type'],
'problem_optimization', 'stt_model_enable', 'stt_model_enable', 'tts_type',
'work_flow'],
properties={
'name': openapi.Schema(type=openapi.TYPE_STRING, title=_("Application Name"),
description=_("Application Name")),
Expand Down Expand Up @@ -361,7 +362,8 @@ def get_request_body_api():
'tts_model_enable': openapi.Schema(type=openapi.TYPE_STRING, title=_("Is text-to-speech enabled"),
description=_("Is text-to-speech enabled")),
'tts_type': openapi.Schema(type=openapi.TYPE_STRING, title=_("Text-to-speech type"),
description=_("Text-to-speech type"))
description=_("Text-to-speech type")),
'work_flow': ApplicationApi.WorkFlow.get_request_body_api(),
}
)

Expand Down Expand Up @@ -432,4 +434,4 @@ def get_request_body_api():
'text': openapi.Schema(type=openapi.TYPE_STRING, title=_("Text"),
description=_("Text")),
}
)
)
28 changes: 28 additions & 0 deletions apps/application/swagger_api/chat_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,34 @@ def get_request_params_api():
description=_('Application ID'))
]

class Operate(ApiMixin):
@staticmethod
def get_request_params_api():
return [openapi.Parameter(name='application_id',
in_=openapi.IN_PATH,
type=openapi.TYPE_STRING,
required=True,
description=_('Application ID')),
openapi.Parameter(name='chat_id',
in_=openapi.IN_PATH,
type=openapi.TYPE_STRING,
required=True,
description=_('Conversation ID')),
]

class ReAbstract(ApiMixin):
@staticmethod
def get_request_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['abstract'],
properties={
'abstract': openapi.Schema(type=openapi.TYPE_STRING, title=_("abstract"),
description=_("abstract"))

}
)


class OpenAIChatApi(ApiMixin):
@staticmethod
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code snippet looks generally structured but contains some areas that could benefit from improvements, such as documentation clarity and adherence to best practices. Here's a review with suggestions:

General Comments:

  1. Class Structure: The use of nested classes Operate and ReAbstract inside OpenAIChatApi is fine, but consider if they follow logical grouping or if there might be better names for them.

  2. Parameter Descriptions: Ensure that all parameters have appropriate descriptions, especially those related to input paths (in_=) and request bodies. Use consistent naming conventions (e.g., description, not _description) throughout the codebase.

  3. Type Specification: Make sure all types specified in OpenAPI schema are correctly defined according to their data structures. For example, strings should clearly define character limits or format constraints if necessary.

  4. Documentation String Consistency: Ensure consistency in the docstring formatting across the entire code base. It’s important for developers to understand at a glance what each function does.

Implementation Suggestions:

get_request_params_api()

@staticmethod
def get_request_params_api():
    return [
        openapi.Parameter(
            name='application_id', 
            in_=openapi.IN_PATH, 
            type=openapi.TYPE_STRING, 
            required=True, 
            description="Application identifier"
        ),
        openapi.Parameter(
            name='chat_id',
            in_=openapi.IN_PATH,
            type=openapi.TYPE_STRING,
            required=True,
            description="Conversation identifier"
        )
    ]

OpenAIChatApi

Maintain similar structure within OpenAIChatApi. Consider adding comments or brief explanations where necessary around complex logic blocks.

Additional Improvements (Optional):

  • Imports: If you haven't already included imports for libraries like flask_openapi_v3.api_doc, ensure these are appropriately imported.

  • Fluent API Usage: In cases where defining many parameters or schemas can become verbose, consider using fluent methods to construct your specs.

By addressing these points, you'll create more maintainable, self-documenting, and robust OpenAPI specifications.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks mostly correct with minor improvements to readability and clarity. Here are some suggestions:

  1. Consistent Functionality Naming: The get_request_params_api method is used consistently across different classes.
  2. Docstrings: Ensure that all docstrings provide clear descriptions of what each part of the API does.
  3. Indentation Consistency: Minor indentation inconsistencies can be cleaned up for better readability.

Here's a slightly revised version of the code with these considerations:

@@ -23,6 +23,34 @@ def get_request_params_api():
                         description=_('Application ID'))
                 ]
 
+class Operate(ApiMixin):
+    @staticmethod
+    def get_request_params_api():
+        """
+        Get request parameters for the Operate endpoint.
+        """
+        return [
+            openapi.Parameter(name='application_id', in_=openapi.IN_PATH, type=openapi.TYPE_STRING, required=True,
+                             description=_('Application ID')),
+            openapi.Parameter(name='chat_id', in_=openapi.IN_PATH, type=openapi.TYPE_STRING, required=True,
+                             description=_('Conversation ID')),
+        ]
+
+    class ReAbstract(ApiMixin):
+        @staticmethod
+        def get_request_body_api():
+            """
+            Define the schema for reabstracting content.
+            """
+            return openapi.Schema(
+                type=openapi.TYPE_OBJECT,
+                required=['abstract'],
+                properties={
+                    'abstract': openapi.Schema(type=openapi.TYPE_STRING, title=_("abstract"),
+                                               description=_("abstract"))
+                }
+            )
 
 class OpenAPIChatApi(ApiMixin):
     @staticmethod

These changes improve the maintainability and readability of the code while maintaining its functionality.

Expand Down
19 changes: 18 additions & 1 deletion apps/application/views/chat_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def post(self, request: Request, chat_id: str):
operation_id=_("Get the conversation list"),
manual_parameters=ChatApi.get_request_params_api(),
responses=result.get_api_array_response(ChatApi.get_response_body_api()),
tags=[_("Application/Conversation Log")]
tags=[_("Application/Conversation Log")]
)
@has_permissions(
ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_KEY],
Expand Down Expand Up @@ -222,6 +222,23 @@ def delete(self, request: Request, application_id: str, chat_id: str):
data={'application_id': application_id, 'user_id': request.user.id,
'chat_id': chat_id}).logic_delete())

@action(methods=['PUT'], detail=False)
@swagger_auto_schema(operation_summary=_("Client modifies dialogue summary"),
operation_id=_("Client modifies dialogue summary"),
request_body=ChatClientHistoryApi.Operate.ReAbstract.get_request_body_api(),
tags=[_("Application/Conversation Log")])
@has_permissions(ViewPermission(
[RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
dynamic_tag=keywords.get('application_id'))],
compare=CompareConstants.AND),
compare=CompareConstants.AND)
def put(self, request: Request, application_id: str, chat_id: str):
return result.success(
ChatSerializers.Operate(
data={'application_id': application_id, 'user_id': request.user.id,
'chat_id': chat_id}).re_abstract(request.data))

class Page(APIView):
authentication_classes = [TokenAuth]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code appears mostly clean and functional. However, I do have a few optimizations and suggestions:

  1. Redundant Import: You can simplify request import by removing the line from fastapi import Request.

    from fastapi.responses import JSONResponse
    from fastapi.openapi.models import APIRoute, Operation, Tag, Parameter
  2. Swagger Auto Schema Decorator Duplication: The @swagger_auto_schema decorator is duplicated between POST and PUT methods in the same endpoint. This redundancy can be removed.

    # Duplicate decorator moved to a separate function
    
    def common_swagger_auto_schema(**kwargs):
        return swagger_auto_schema(
            operation_summary=kwargs.pop('operation_summary', None),
            operation_id=kwargs.pop('operation_id', None),
            request_body=kwargs.pop('request-body', {}),
            tags=kwargs.pop('tags', []),
            *args,
            **kwargs
        )
    
    @post(...)
    @common_swagger_auto_schema()
    ...```
  3. Logic Deletion Method Suggestion: Ensure the logic deletion method (logic_delete()) is correctly implemented and returns the expected response. If it's not returning an appropriate type, adjust its implementation accordingly.

  4. PUT Method Return Value Suggestion: For the put method, ensure that result.success(...) correctly handles the re-abstracting process with user ID and chat ID as parameters. Adjust the serializer usage if necessary.

  5. Docstring Review: Add or update docstrings for all functions that explain their purpose, inputs, outputs, and usage. This improves readability and maintainability.

These suggested changes should help optimize and clarify the given codebase.

Expand Down
2 changes: 1 addition & 1 deletion apps/common/util/function_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, sandbox=False):
self.user = None
self._createdir()
if self.sandbox:
os.system(f"chown -R {self.user}:{self.user} {self.sandbox_path}")
os.system(f"chown -R {self.user}:root {self.sandbox_path}")

def _createdir(self):
old_mask = os.umask(0o077)
Expand Down
2 changes: 2 additions & 0 deletions apps/locales/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -6753,3 +6753,5 @@ msgstr ""
msgid "Image download failed, check network"
msgstr ""

msgid "Client modifies dialogue summary"
msgstr ""
3 changes: 2 additions & 1 deletion apps/locales/zh_CN/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -6892,4 +6892,5 @@ msgstr "超出许可证使用限制。"
msgid "Image download failed, check network"
msgstr "图片下载失败,请检查网络"


msgid "Client modifies dialogue summary"
msgstr "客户端修改对话摘要"
5 changes: 4 additions & 1 deletion apps/locales/zh_Hant/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -6902,4 +6902,7 @@ msgid "License usage limit exceeded."
msgstr "超出許可證使用限制。"

msgid "Image download failed, check network"
msgstr "圖片下載失敗,檢查網絡"
msgstr "圖片下載失敗,檢查網絡"

msgid "Client modifies dialogue summary"
msgstr "用戶端修改對話摘要"
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,3 @@ def build_model(self, model_info: Dict[str, object]):
return self

api_base = forms.TextInputField('API URL', required=True)
api_key = forms.TextInputField('API Key', required=True)
Loading