mirror of
https://gitee.com/dify_ai/dify.git
synced 2024-12-02 19:27:48 +08:00
langfuser add view button (#7571)
This commit is contained in:
parent
6025002971
commit
9864b35465
@ -21,6 +21,7 @@ class LangfuseConfig(BaseTracingConfig):
|
|||||||
"""
|
"""
|
||||||
public_key: str
|
public_key: str
|
||||||
secret_key: str
|
secret_key: str
|
||||||
|
project_key: str
|
||||||
host: str = 'https://api.langfuse.com'
|
host: str = 'https://api.langfuse.com'
|
||||||
|
|
||||||
@field_validator("host")
|
@field_validator("host")
|
||||||
|
@ -419,3 +419,11 @@ class LangFuseDataTrace(BaseTraceInstance):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"LangFuse API check failed: {str(e)}")
|
logger.debug(f"LangFuse API check failed: {str(e)}")
|
||||||
raise ValueError(f"LangFuse API check failed: {str(e)}")
|
raise ValueError(f"LangFuse API check failed: {str(e)}")
|
||||||
|
|
||||||
|
def get_project_key(self):
|
||||||
|
try:
|
||||||
|
projects = self.langfuse_client.client.projects.get()
|
||||||
|
return projects.data[0].id
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"LangFuse get project key failed: {str(e)}")
|
||||||
|
raise ValueError(f"LangFuse get project key failed: {str(e)}")
|
||||||
|
@ -38,7 +38,7 @@ provider_config_map = {
|
|||||||
TracingProviderEnum.LANGFUSE.value: {
|
TracingProviderEnum.LANGFUSE.value: {
|
||||||
'config_class': LangfuseConfig,
|
'config_class': LangfuseConfig,
|
||||||
'secret_keys': ['public_key', 'secret_key'],
|
'secret_keys': ['public_key', 'secret_key'],
|
||||||
'other_keys': ['host'],
|
'other_keys': ['host', 'project_key'],
|
||||||
'trace_instance': LangFuseDataTrace
|
'trace_instance': LangFuseDataTrace
|
||||||
},
|
},
|
||||||
TracingProviderEnum.LANGSMITH.value: {
|
TracingProviderEnum.LANGSMITH.value: {
|
||||||
@ -123,7 +123,6 @@ class OpsTraceManager:
|
|||||||
|
|
||||||
for key in other_keys:
|
for key in other_keys:
|
||||||
new_config[key] = decrypt_tracing_config.get(key, "")
|
new_config[key] = decrypt_tracing_config.get(key, "")
|
||||||
|
|
||||||
return config_class(**new_config).model_dump()
|
return config_class(**new_config).model_dump()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -252,6 +251,19 @@ class OpsTraceManager:
|
|||||||
tracing_config = config_type(**tracing_config)
|
tracing_config = config_type(**tracing_config)
|
||||||
return trace_instance(tracing_config).api_check()
|
return trace_instance(tracing_config).api_check()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_trace_config_project_key(tracing_config: dict, tracing_provider: str):
|
||||||
|
"""
|
||||||
|
get trace config is project key
|
||||||
|
:param tracing_config: tracing config
|
||||||
|
:param tracing_provider: tracing provider
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
config_type, trace_instance = provider_config_map[tracing_provider]['config_class'], \
|
||||||
|
provider_config_map[tracing_provider]['trace_instance']
|
||||||
|
tracing_config = config_type(**tracing_config)
|
||||||
|
return trace_instance(tracing_config).get_project_key()
|
||||||
|
|
||||||
|
|
||||||
class TraceTask:
|
class TraceTask:
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -22,6 +22,10 @@ class OpsService:
|
|||||||
# decrypt_token and obfuscated_token
|
# decrypt_token and obfuscated_token
|
||||||
tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id
|
tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id
|
||||||
decrypt_tracing_config = OpsTraceManager.decrypt_tracing_config(tenant_id, tracing_provider, trace_config_data.tracing_config)
|
decrypt_tracing_config = OpsTraceManager.decrypt_tracing_config(tenant_id, tracing_provider, trace_config_data.tracing_config)
|
||||||
|
if tracing_provider == 'langfuse' and ('project_key' not in decrypt_tracing_config or not decrypt_tracing_config.get('project_key')):
|
||||||
|
project_key = OpsTraceManager.get_trace_config_project_key(decrypt_tracing_config, tracing_provider)
|
||||||
|
decrypt_tracing_config['project_key'] = project_key
|
||||||
|
|
||||||
decrypt_tracing_config = OpsTraceManager.obfuscated_decrypt_token(tracing_provider, decrypt_tracing_config)
|
decrypt_tracing_config = OpsTraceManager.obfuscated_decrypt_token(tracing_provider, decrypt_tracing_config)
|
||||||
|
|
||||||
trace_config_data.tracing_config = decrypt_tracing_config
|
trace_config_data.tracing_config = decrypt_tracing_config
|
||||||
@ -37,7 +41,7 @@ class OpsService:
|
|||||||
:param tracing_config: tracing config
|
:param tracing_config: tracing config
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if tracing_provider not in provider_config_map.keys() and tracing_provider != None:
|
if tracing_provider not in provider_config_map.keys() and tracing_provider:
|
||||||
return {"error": f"Invalid tracing provider: {tracing_provider}"}
|
return {"error": f"Invalid tracing provider: {tracing_provider}"}
|
||||||
|
|
||||||
config_class, other_keys = provider_config_map[tracing_provider]['config_class'], \
|
config_class, other_keys = provider_config_map[tracing_provider]['config_class'], \
|
||||||
@ -51,6 +55,9 @@ class OpsService:
|
|||||||
if not OpsTraceManager.check_trace_config_is_effective(tracing_config, tracing_provider):
|
if not OpsTraceManager.check_trace_config_is_effective(tracing_config, tracing_provider):
|
||||||
return {"error": "Invalid Credentials"}
|
return {"error": "Invalid Credentials"}
|
||||||
|
|
||||||
|
# get project key
|
||||||
|
project_key = OpsTraceManager.get_trace_config_project_key(tracing_config, tracing_provider)
|
||||||
|
|
||||||
# check if trace config already exists
|
# check if trace config already exists
|
||||||
trace_config_data: TraceAppConfig = db.session.query(TraceAppConfig).filter(
|
trace_config_data: TraceAppConfig = db.session.query(TraceAppConfig).filter(
|
||||||
TraceAppConfig.app_id == app_id, TraceAppConfig.tracing_provider == tracing_provider
|
TraceAppConfig.app_id == app_id, TraceAppConfig.tracing_provider == tracing_provider
|
||||||
@ -62,6 +69,8 @@ class OpsService:
|
|||||||
# get tenant id
|
# get tenant id
|
||||||
tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id
|
tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id
|
||||||
tracing_config = OpsTraceManager.encrypt_tracing_config(tenant_id, tracing_provider, tracing_config)
|
tracing_config = OpsTraceManager.encrypt_tracing_config(tenant_id, tracing_provider, tracing_config)
|
||||||
|
if tracing_provider == 'langfuse':
|
||||||
|
tracing_config['project_key'] = project_key
|
||||||
trace_config_data = TraceAppConfig(
|
trace_config_data = TraceAppConfig(
|
||||||
app_id=app_id,
|
app_id=app_id,
|
||||||
tracing_provider=tracing_provider,
|
tracing_provider=tracing_provider,
|
||||||
|
@ -6,6 +6,7 @@ import { TracingProvider } from './type'
|
|||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { LangfuseIconBig, LangsmithIconBig } from '@/app/components/base/icons/src/public/tracing'
|
import { LangfuseIconBig, LangsmithIconBig } from '@/app/components/base/icons/src/public/tracing'
|
||||||
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
|
import { Eye as View } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
|
|
||||||
const I18N_PREFIX = 'app.tracing'
|
const I18N_PREFIX = 'app.tracing'
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ type Props = {
|
|||||||
type: TracingProvider
|
type: TracingProvider
|
||||||
readOnly: boolean
|
readOnly: boolean
|
||||||
isChosen: boolean
|
isChosen: boolean
|
||||||
|
Config: any
|
||||||
onChoose: () => void
|
onChoose: () => void
|
||||||
hasConfigured: boolean
|
hasConfigured: boolean
|
||||||
onConfig: () => void
|
onConfig: () => void
|
||||||
@ -29,6 +31,7 @@ const ProviderPanel: FC<Props> = ({
|
|||||||
type,
|
type,
|
||||||
readOnly,
|
readOnly,
|
||||||
isChosen,
|
isChosen,
|
||||||
|
Config,
|
||||||
onChoose,
|
onChoose,
|
||||||
hasConfigured,
|
hasConfigured,
|
||||||
onConfig,
|
onConfig,
|
||||||
@ -41,6 +44,14 @@ const ProviderPanel: FC<Props> = ({
|
|||||||
onConfig()
|
onConfig()
|
||||||
}, [onConfig])
|
}, [onConfig])
|
||||||
|
|
||||||
|
const viewBtnClick = useCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const url = `${Config?.host}/project/${Config?.project_key}`
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
}, [Config?.host, Config?.project_key])
|
||||||
|
|
||||||
const handleChosen = useCallback((e: React.MouseEvent) => {
|
const handleChosen = useCallback((e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (isChosen || !hasConfigured || readOnly)
|
if (isChosen || !hasConfigured || readOnly)
|
||||||
@ -58,12 +69,20 @@ const ProviderPanel: FC<Props> = ({
|
|||||||
{isChosen && <div className='ml-1 flex items-center h-4 px-1 rounded-[4px] border border-primary-500 leading-4 text-xs font-medium text-primary-500 uppercase '>{t(`${I18N_PREFIX}.inUse`)}</div>}
|
{isChosen && <div className='ml-1 flex items-center h-4 px-1 rounded-[4px] border border-primary-500 leading-4 text-xs font-medium text-primary-500 uppercase '>{t(`${I18N_PREFIX}.inUse`)}</div>}
|
||||||
</div>
|
</div>
|
||||||
{!readOnly && (
|
{!readOnly && (
|
||||||
<div
|
<div className={'flex justify-between items-center space-x-1'}>
|
||||||
className='flex px-2 items-center h-6 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer text-gray-700 space-x-1'
|
{hasConfigured && (
|
||||||
onClick={handleConfigBtnClick}
|
<div className='flex px-2 items-center h-6 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer text-gray-700 space-x-1' onClick={viewBtnClick} >
|
||||||
>
|
<View className='w-3 h-3'/>
|
||||||
<Settings04 className='w-3 h-3' />
|
<div className='text-xs font-medium'>{t(`${I18N_PREFIX}.view`)}</div>
|
||||||
<div className='text-xs font-medium'>{t(`${I18N_PREFIX}.config`)}</div>
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className='flex px-2 items-center h-6 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer text-gray-700 space-x-1'
|
||||||
|
onClick={handleConfigBtnClick}
|
||||||
|
>
|
||||||
|
<Settings04 className='w-3 h-3' />
|
||||||
|
<div className='text-xs font-medium'>{t(`${I18N_PREFIX}.config`)}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ const translation = {
|
|||||||
title: 'Tracing app performance',
|
title: 'Tracing app performance',
|
||||||
description: 'Configuring a Third-Party LLMOps provider and tracing app performance.',
|
description: 'Configuring a Third-Party LLMOps provider and tracing app performance.',
|
||||||
config: 'Config',
|
config: 'Config',
|
||||||
|
view: 'View',
|
||||||
collapse: 'Collapse',
|
collapse: 'Collapse',
|
||||||
expand: 'Expand',
|
expand: 'Expand',
|
||||||
tracing: 'Tracing',
|
tracing: 'Tracing',
|
||||||
|
@ -94,6 +94,7 @@ const translation = {
|
|||||||
title: '追踪应用性能',
|
title: '追踪应用性能',
|
||||||
description: '配置第三方 LLMOps 提供商并跟踪应用程序性能。',
|
description: '配置第三方 LLMOps 提供商并跟踪应用程序性能。',
|
||||||
config: '配置',
|
config: '配置',
|
||||||
|
view: '查看',
|
||||||
collapse: '折叠',
|
collapse: '折叠',
|
||||||
expand: '展开',
|
expand: '展开',
|
||||||
tracing: '追踪',
|
tracing: '追踪',
|
||||||
|
@ -90,6 +90,7 @@ const translation = {
|
|||||||
title: '追蹤應用程式效能',
|
title: '追蹤應用程式效能',
|
||||||
description: '配置第三方LLMOps提供商並追蹤應用程式效能。',
|
description: '配置第三方LLMOps提供商並追蹤應用程式效能。',
|
||||||
config: '配置',
|
config: '配置',
|
||||||
|
view: '查看',
|
||||||
collapse: '收起',
|
collapse: '收起',
|
||||||
expand: '展開',
|
expand: '展開',
|
||||||
tracing: '追蹤',
|
tracing: '追蹤',
|
||||||
|
Loading…
Reference in New Issue
Block a user