Merge branch 'langgenius:main' into fix/manual-metadata-conditon-in-agent

pull/20362/head
suntp 12 months ago committed by GitHub
commit 1ab4b1e377
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -452,7 +452,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
for var, val in context.items(): for var, val in context.items():
var.set(val) var.set(val)
# Save current user before entering new app context # FIXME(-LAN-): Save current user before entering new app context
from flask import g from flask import g
saved_user = None saved_user = None

@ -232,7 +232,7 @@ class AgentChatAppGenerator(MessageBasedAppGenerator):
for var, val in context.items(): for var, val in context.items():
var.set(val) var.set(val)
# Save current user before entering new app context # FIXME(-LAN-): Save current user before entering new app context
from flask import g from flask import g
saved_user = None saved_user = None

@ -411,7 +411,7 @@ class WorkflowAppGenerator(BaseAppGenerator):
for var, val in context.items(): for var, val in context.items():
var.set(val) var.set(val)
# Save current user before entering new app context # FIXME(-LAN-): Save current user before entering new app context
from flask import g from flask import g
saved_user = None saved_user = None

@ -455,8 +455,6 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan
agent_thought: Optional[MessageAgentThought] = ( agent_thought: Optional[MessageAgentThought] = (
db.session.query(MessageAgentThought).filter(MessageAgentThought.id == event.agent_thought_id).first() db.session.query(MessageAgentThought).filter(MessageAgentThought.id == event.agent_thought_id).first()
) )
db.session.refresh(agent_thought)
db.session.close()
if agent_thought: if agent_thought:
return AgentThoughtStreamResponse( return AgentThoughtStreamResponse(

@ -9,7 +9,7 @@ from copy import copy, deepcopy
from datetime import UTC, datetime from datetime import UTC, datetime
from typing import Any, Optional, cast from typing import Any, Optional, cast
from flask import Flask, current_app from flask import Flask, current_app, has_request_context
from configs import dify_config from configs import dify_config
from core.app.apps.base_app_queue_manager import GenerateTaskStoppedError from core.app.apps.base_app_queue_manager import GenerateTaskStoppedError
@ -540,8 +540,21 @@ class GraphEngine:
for var, val in context.items(): for var, val in context.items():
var.set(val) var.set(val)
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None
if has_request_context() and hasattr(g, "_login_user"):
saved_user = g._login_user
with flask_app.app_context(): with flask_app.app_context():
try: try:
# Restore user in new app context
if saved_user is not None:
from flask import g
g._login_user = saved_user
q.put( q.put(
ParallelBranchRunStartedEvent( ParallelBranchRunStartedEvent(
parallel_id=parallel_id, parallel_id=parallel_id,

@ -235,6 +235,10 @@ class Executor:
files[key].append(file_tuple) files[key].append(file_tuple)
# convert files to list for httpx request # convert files to list for httpx request
# If there are no actual files, we still need to force httpx to use `multipart/form-data`.
# This is achieved by inserting a harmless placeholder file that will be ignored by the server.
if not files:
self.files = [("__multipart_placeholder__", ("", b"", "application/octet-stream"))]
if files: if files:
self.files = [] self.files = []
for key, file_tuples in files.items(): for key, file_tuples in files.items():
@ -373,7 +377,10 @@ class Executor:
raw += f"{k}: {v}\r\n" raw += f"{k}: {v}\r\n"
body_string = "" body_string = ""
if self.files: # Only log actual files if present.
# '__multipart_placeholder__' is inserted to force multipart encoding but is not a real file.
# This prevents logging meaningless placeholder entries.
if self.files and not all(f[0] == "__multipart_placeholder__" for f in self.files):
for key, (filename, content, mime_type) in self.files: for key, (filename, content, mime_type) in self.files:
body_string += f"--{boundary}\r\n" body_string += f"--{boundary}\r\n"
body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n'

@ -7,7 +7,7 @@ from datetime import UTC, datetime
from queue import Empty, Queue from queue import Empty, Queue
from typing import TYPE_CHECKING, Any, Optional, cast from typing import TYPE_CHECKING, Any, Optional, cast
from flask import Flask, current_app from flask import Flask, current_app, has_request_context
from configs import dify_config from configs import dify_config
from core.variables import ArrayVariable, IntegerVariable, NoneVariable from core.variables import ArrayVariable, IntegerVariable, NoneVariable
@ -586,7 +586,21 @@ class IterationNode(BaseNode[IterationNodeData]):
""" """
for var, val in context.items(): for var, val in context.items():
var.set(val) var.set(val)
# FIXME(-LAN-): Save current user before entering new app context
from flask import g
saved_user = None
if has_request_context() and hasattr(g, "_login_user"):
saved_user = g._login_user
with flask_app.app_context(): with flask_app.app_context():
# Restore user in new app context
if saved_user is not None:
from flask import g
g._login_user = saved_user
parallel_mode_run_id = uuid.uuid4().hex parallel_mode_run_id = uuid.uuid4().hex
graph_engine_copy = graph_engine.create_copy() graph_engine_copy = graph_engine.create_copy()
variable_pool_copy = graph_engine_copy.graph_runtime_state.variable_pool variable_pool_copy = graph_engine_copy.graph_runtime_state.variable_pool

@ -22,6 +22,7 @@ def on_user_loaded(_sender, user: Union["Account", "EndUser"]):
from opentelemetry.trace import get_current_span from opentelemetry.trace import get_current_span
if user: if user:
try:
current_span = get_current_span() current_span = get_current_span()
if isinstance(user, Account) and user.current_tenant_id: if isinstance(user, Account) and user.current_tenant_id:
tenant_id = user.current_tenant_id tenant_id = user.current_tenant_id
@ -32,6 +33,9 @@ def on_user_loaded(_sender, user: Union["Account", "EndUser"]):
if current_span: if current_span:
current_span.set_attribute("service.tenant.id", tenant_id) current_span.set_attribute("service.tenant.id", tenant_id)
current_span.set_attribute("service.user.id", user.id) current_span.set_attribute("service.user.id", user.id)
except Exception:
logging.exception("Error setting tenant and user attributes")
pass
def init_app(app: DifyApp): def init_app(app: DifyApp):
@ -54,6 +58,7 @@ def init_app(app: DifyApp):
def response_hook(span: Span, status: str, response_headers: list): def response_hook(span: Span, status: str, response_headers: list):
if span and span.is_recording(): if span and span.is_recording():
try:
if status.startswith("2"): if status.startswith("2"):
span.set_status(StatusCode.OK) span.set_status(StatusCode.OK)
else: else:
@ -69,6 +74,9 @@ def init_app(app: DifyApp):
if request and request.method: if request and request.method:
attributes[SpanAttributes.HTTP_METHOD] = str(request.method) attributes[SpanAttributes.HTTP_METHOD] = str(request.method)
_http_response_counter.add(1, attributes) _http_response_counter.add(1, attributes)
except Exception:
logging.exception("Error setting status and attributes")
pass
instrumentor = FlaskInstrumentor() instrumentor = FlaskInstrumentor()
if dify_config.DEBUG: if dify_config.DEBUG:
@ -99,7 +107,7 @@ def init_app(app: DifyApp):
class ExceptionLoggingHandler(logging.Handler): class ExceptionLoggingHandler(logging.Handler):
"""Custom logging handler that creates spans for logging.exception() calls""" """Custom logging handler that creates spans for logging.exception() calls"""
def emit(self, record): def emit(self, record: logging.LogRecord):
try: try:
if record.exc_info: if record.exc_info:
tracer = get_tracer_provider().get_tracer("dify.exception.logging") tracer = get_tracer_provider().get_tracer("dify.exception.logging")
@ -114,9 +122,12 @@ def init_app(app: DifyApp):
}, },
) as span: ) as span:
span.set_status(StatusCode.ERROR) span.set_status(StatusCode.ERROR)
if record.exc_info[1]:
span.record_exception(record.exc_info[1]) span.record_exception(record.exc_info[1])
span.set_attribute("exception.type", record.exc_info[0].__name__)
span.set_attribute("exception.message", str(record.exc_info[1])) span.set_attribute("exception.message", str(record.exc_info[1]))
if record.exc_info[0]:
span.set_attribute("exception.type", record.exc_info[0].__name__)
except Exception: except Exception:
pass pass

@ -246,7 +246,9 @@ def test_executor_with_form_data():
assert "multipart/form-data" in executor.headers["Content-Type"] assert "multipart/form-data" in executor.headers["Content-Type"]
assert executor.params == [] assert executor.params == []
assert executor.json is None assert executor.json is None
assert executor.files is None # '__multipart_placeholder__' is expected when no file inputs exist,
# to ensure the request is treated as multipart/form-data by the backend.
assert executor.files == [("__multipart_placeholder__", ("", b"", "application/octet-stream"))]
assert executor.content is None assert executor.content is None
# Check that the form data is correctly loaded in executor.data # Check that the form data is correctly loaded in executor.data

@ -105,7 +105,7 @@ const ChangePasswordForm = () => {
</div> </div>
<div className="mx-auto mt-6 w-full"> <div className="mx-auto mt-6 w-full">
<div className="bg-white"> <div>
{/* Password */} {/* Password */}
<div className='mb-5'> <div className='mb-5'>
<label htmlFor="password" className="system-md-semibold my-2 text-text-secondary"> <label htmlFor="password" className="system-md-semibold my-2 text-text-secondary">

@ -645,9 +645,9 @@ const translation = {
perPage: '페이지당 항목 수', perPage: '페이지당 항목 수',
}, },
theme: { theme: {
theme: '주제', theme: '테마',
light: '', light: '밝은',
dark: '어', dark: '어두운',
auto: '시스템', auto: '시스템',
}, },
compliance: { compliance: {

@ -301,7 +301,7 @@ const translation = {
designDocument: '디자인 문서', designDocument: '디자인 문서',
productSpecification: '제품 사양서', productSpecification: '제품 사양서',
financialReport: '재무 보고서', financialReport: '재무 보고서',
marketAnalysis: '시장 분석', marketAnalysis: '마켓 분석',
projectPlan: '프로젝트 계획서', projectPlan: '프로젝트 계획서',
teamStructure: '팀 구조', teamStructure: '팀 구조',
policiesProcedures: '정책 및 절차', policiesProcedures: '정책 및 절차',

@ -194,7 +194,7 @@ const translation = {
datasetMetadata: { datasetMetadata: {
name: '이름', name: '이름',
deleteTitle: '삭제 확인', deleteTitle: '삭제 확인',
disabled: '장애인', disabled: '사용안함',
addMetaData: '메타데이터 추가', addMetaData: '메타데이터 추가',
values: '{{num}} 값들', values: '{{num}} 값들',
namePlaceholder: '메타데이터 이름', namePlaceholder: '메타데이터 이름',

@ -25,7 +25,7 @@ const translation = {
}, },
source: { source: {
local: '로컬 패키지 파일', local: '로컬 패키지 파일',
marketplace: '시장', marketplace: '마켓',
github: '깃허브', github: '깃허브',
}, },
detailPanel: { detailPanel: {
@ -197,7 +197,7 @@ const translation = {
endpointsEnabled: '{{num}}개의 엔드포인트 집합이 활성화되었습니다.', endpointsEnabled: '{{num}}개의 엔드포인트 집합이 활성화되었습니다.',
installFrom: '에서 설치', installFrom: '에서 설치',
allCategories: '모든 카테고리', allCategories: '모든 카테고리',
submitPlugin: '제출 플러그인', submitPlugin: '플러그인 제출',
findMoreInMarketplace: 'Marketplace에서 더 알아보기', findMoreInMarketplace: 'Marketplace에서 더 알아보기',
searchCategories: '검색 카테고리', searchCategories: '검색 카테고리',
search: '검색', search: '검색',

@ -200,7 +200,7 @@ const translation = {
code: '코드', code: '코드',
model: '모델', model: '모델',
rerankModel: '재정렬 모델', rerankModel: '재정렬 모델',
visionVariable: '시력 변수', visionVariable: '비전 변수',
}, },
invalidVariable: '잘못된 변수', invalidVariable: '잘못된 변수',
rerankModelRequired: 'Rerank Model을 켜기 전에 설정에서 모델이 성공적으로 구성되었는지 확인하십시오.', rerankModelRequired: 'Rerank Model을 켜기 전에 설정에서 모델이 성공적으로 구성되었는지 확인하십시오.',
@ -463,7 +463,7 @@ const translation = {
metadata: { metadata: {
options: { options: {
disabled: { disabled: {
title: '장애인', title: '사용안함',
subTitle: '메타데이터 필터링을 활성화하지 않음', subTitle: '메타데이터 필터링을 활성화하지 않음',
}, },
automatic: { automatic: {

Loading…
Cancel
Save