Compare commits

..

70 Commits

Author SHA1 Message Date
dacd480c6f Update Amplitude integration and dependencies
- Replaced @amplitude/unified with @amplitude/analytics-browser and added @amplitude/plugin-session-replay-browser for enhanced tracking capabilities.
- Modified AmplitudeProvider to include session replay functionality with a configurable sample rate.
- Updated user identification in AppContextProvider to use user email instead of ID for tracking.
- Bumped various dependencies in package.json and pnpm-lock.yaml for compatibility and improvements.
2025-11-25 13:49:13 +08:00
d960d3ee3e Add Amplitude analytics integration and tracking (#27890)
- Introduced AmplitudeProvider component for initializing Amplitude analytics.
- Integrated user tracking in AppContextProvider to report user and workspace information.
- Added event tracking for workflow log filter selections.
- Updated package.json and pnpm-lock.yaml to include @amplitude/unified dependency.
2025-11-14 14:45:36 +08:00
898e65d645 fix: card view render (#28189) 2025-11-14 14:43:41 +08:00
818966e53f feat: add icon gallery stories (#28214)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com>
2025-11-14 14:43:41 +08:00
e285917e37 fix: knowledge pipeline can not published (#28203) 2025-11-14 14:43:41 +08:00
d18b2c0e82 Upgrade weave version to fix weave configuration failure (#28197) 2025-11-14 14:43:41 +08:00
d205b4c10e chore: bump to 1.10.0 (#28186)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-11-14 14:43:41 +08:00
4bd114a90a refactor(web): remove redundant add-tool-modal components and related code (#27996) 2025-11-14 14:43:41 +08:00
84a9ddc2d8 fix: StreamableHTTPTransport got invalid json exception when receive a ping event from mcp server #28111 (#28116) 2025-11-14 14:43:41 +08:00
e658a6a80d TypedBase + TypedDict (#28137)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 14:43:41 +08:00
6f93e2f780 minor fix(rag): return early when pushing empty tasks to avoid Redis DataError (#28027)
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
2025-11-14 14:43:41 +08:00
37fc386af2 fix(web): fix unit promotion in formatNumberAbbreviated (#27918)
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
2025-11-14 14:43:41 +08:00
9aa9c87e0d fix: adjust padding in AgentNode and NodeComponent for consistent layout (#28175) 2025-11-14 14:43:41 +08:00
56e9129efb fix: update start-worker alias to include additional queues for bette… (#28179) 2025-11-14 14:43:41 +08:00
1e2311128c fix: update package manager version to 10.22.0 (#28181) 2025-11-14 14:43:41 +08:00
08edf20672 feat: enhance start node metadata to be undeletable in chat mode (#28173) 2025-11-14 14:43:40 +08:00
28626590d1 fix: skip tests if no database run (#28102)
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
8dc5d98174 Fix: Correctly handle merged cells in DOCX tables to prevent content duplication and loss (#27871)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
9c10731494 feat: add draft trigger detection to app model and UI (#28163)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
eaed72a40c fix(knowledge-base): regenerate child chunks not working completely (#27934) 2025-11-14 14:43:40 +08:00
168a1524b2 fix: can still invite when api is pending (#28161) 2025-11-14 14:43:40 +08:00
8e01eb4561 fix(moderation): change OpenAI moderation model to omni-moderation-la… (#28119)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
96865ebc8c fix: fixed error when clear value of INTEGER and FLOAT type (#27954)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
7ca5e8812e fix: missing pipeline_templates.json when HOSTED_FETCH_PIPELINE_TEMPLATES_MODE is builtin (#27946)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
201fa723a5 fix: update input field width for retry configuration in RetryOnPanel (#28142) 2025-11-14 14:43:40 +08:00
32b5ae3b71 fix: correct spelling of "模板" in translation files (#28151) 2025-11-14 14:43:40 +08:00
f3bfccbd67 fix: inconsistent behaviour of zoom in button and shortcut (#27944) 2025-11-14 14:43:40 +08:00
a4e3a39dd2 add doc (#28016)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
9e753b031a refactor(web): reuse the same edit-custom-collection-modal component, and fix the pop up error (#28003) 2025-11-14 14:43:40 +08:00
15f3d68bc5 fix: app's ai site text to speech api (#28091) 2025-11-14 14:43:40 +08:00
b9202d1e0a fix: simplify graph structure validation in WorkflowService (#28146)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
a6e9da1d89 chore: disable workflow logs auto-cleanup by default (#28136)
This PR changes the default value of `WORKFLOW_LOG_CLEANUP_ENABLED` from `true` to `false` across all configuration files.

## Motivation

Setting the default to `false` provides safer default behavior by:

- Preventing unintended data loss for new installations
- Giving users explicit control over when to enable log cleanup
- Following the opt-in principle for data deletion features

Users who need automatic cleanup can enable it by setting `WORKFLOW_LOG_CLEANUP_ENABLED=true` in their configuration.
2025-11-14 14:43:40 +08:00
125f37b562 convert to TypeBase (#27935)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 14:43:40 +08:00
eea3f14777 test: create some hooks and utils test script, modified clipboard test script (#27928) 2025-11-14 14:43:40 +08:00
0e43f5ad4d Add file type validation to paste upload (#28017) 2025-11-14 14:43:40 +08:00
e3f85c1658 fix: variable assigner can't assign float number (#28068) 2025-11-14 14:43:40 +08:00
10571fcf7c fix: determine cpu cores determination in baseedpyright-check script on macos (#28058) 2025-11-14 14:43:39 +08:00
244bd08db2 feat: introduce trigger functionality (#27644)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com>
Co-authored-by: Stream <Stream_2@qq.com>
Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: zhsama <torvalds@linux.do>
Co-authored-by: Harry <xh001x@hotmail.com>
Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com>
Co-authored-by: yessenia <yessenia.contact@gmail.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: WTW0313 <twwu@dify.ai>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 14:43:37 +08:00
73ef99c25a add transform-datasource-credentials command online check (#28124)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Garfield Dai <dai.hai@foxmail.com>
2025-11-14 14:43:13 +08:00
d5a30c667b feat(api): Introduce WorkflowResumptionContext for pause state management (#28122)
Certain metadata (including but not limited to `InvokeFrom`, `call_depth`, and `streaming`)  is required when resuming a paused workflow. However, these fields are not part of `GraphRuntimeState` and were not saved in the previous
 implementation of  `PauseStatePersistenceLayer`.

This commit addresses this limitation by introducing a `WorkflowResumptionContext` model that wraps both the `*GenerateEntity` and `GraphRuntimeState`. This approach provides:

- A structured container for all necessary resumption data
- Better separation of concerns between execution state and persistence
- Enhanced extensibility for future metadata additions
- Clearer naming that distinguishes from `GraphRuntimeState`

The `WorkflowResumptionContext` model makes extending the pause state easier while maintaining backward compatibility and proper version management for the entire execution state ecosystem.

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-11-14 14:43:13 +08:00
9648020ab4 chore: improve the user experience of not login into apps (#28120) 2025-11-14 14:43:13 +08:00
76be5c054b fix document index test (#28113) 2025-11-14 14:43:13 +08:00
c76f370f97 When graph_engine worker run exception, keep the node_id for deep res… (#26205)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
2025-11-14 14:43:13 +08:00
853f5ff09e chore: not SaaS version can query long log time range (#28109) 2025-11-14 14:43:13 +08:00
2ee55d8bc8 fix document enable (#28081) 2025-11-14 14:43:12 +08:00
57a7e3c20f chore: add type-check to pre-commit (#28005) 2025-11-14 14:43:12 +08:00
3160242fca fix(api): Trace Hierarchy, Span Status, and Broken Workflow for Arize & Phoenix Integration (#27937)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
001e4f8c69 chore: remove unused enterprise bot from the readme (#28073)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
48c95f4ccf chore: translate i18n files and update type definitions (#28054)
Co-authored-by: iamjoel <2120155+iamjoel@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
1418512f24 chore: Remove trailing space from migration filename (#28040) 2025-11-14 14:43:12 +08:00
f834f8a160 Chore: change query log time range (#28052)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
feaad67531 feat(api): Introduce Broadcast Channel (#27835)
This PR introduces a `BroadcastChannel` abstraction with broadcasting and at-most once delivery semantics, serving as the communication component between celery worker and API server.

It also includes a reference implementation backed by Redis PubSub.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
9592b72aa3 fix workflow default updated_at (#28047) 2025-11-14 14:43:12 +08:00
6325ed9cd3 fix: elasticsearch_vector version (#28028)
Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
ab39f39db0 feat: Add Audio Content Support for MCP Tools (#27979) 2025-11-14 14:43:12 +08:00
becabd84a9 Fix typo in weaviate comment, improve time test precision, and add security tests for get-icon utility (#27919)
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
62de0e97e5 chore(deps): bump scipy-stubs from 1.16.2.3 to 1.16.3.0 in /api (#28025)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
aa0fdbf759 add onupdate=func.current_timestamp() (#28014)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-14 14:43:12 +08:00
c3b48cca11 fix: the model list encountered two children with the same key (#27956)
Co-authored-by: haokai <haokai@shuwen.com>
2025-11-14 14:43:12 +08:00
874ee69f2b fix: fix https://github.com/langgenius/dify/issues/27939 (#27985) 2025-11-14 14:43:12 +08:00
8f499d9894 docs: clarify how to obtain workflow_id for version execution (#28007)
Signed-off-by: OneZero-Y <aukovyps@163.com>
2025-11-14 14:43:12 +08:00
f16a42ee52 fix: bump brotli to 1.2.0 resloved CVE-2025-6176 (#27950)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-11-14 14:43:12 +08:00
9b43b9f1e9 refactor: implement tenant self queue for rag tasks (#27559)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
2025-11-14 14:43:12 +08:00
f8e01d7df8 fix(api): fix VariablePool.get adding unexpected keys to variable_dictionary (#26767)
Co-authored-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 14:43:11 +08:00
ead5a78faa fix: prevent fetch version info in enterprise edition (#27923) 2025-11-14 14:43:10 +08:00
fd2ff963a9 fix jina reader transform (#27922) 2025-11-14 14:42:39 +08:00
7492b39ff0 feat: add registration and create_app tracking 2025-11-14 14:40:53 +08:00
8b5533e1a4 Enhance Amplitude tracking by validating user ID and properties
- Updated middleware to include Amplitude domains.
- Improved setUserId function to handle empty user IDs and log warnings in development.
- Enhanced setUserProperties function to filter out invalid properties and log results in development.
- Refactored user tracking in AppContextProvider to streamline setting user and workspace properties.
2025-11-07 11:22:26 +08:00
fa7a0fd3d1 Implement event tracking for app creation and user actions across various components 2025-11-07 11:11:45 +08:00
2a20a96123 Add Amplitude analytics integration and tracking (#27890)
- Introduced AmplitudeProvider component for initializing Amplitude analytics.
- Integrated user tracking in AppContextProvider to report user and workspace information.
- Added event tracking for workflow log filter selections.
- Updated package.json and pnpm-lock.yaml to include @amplitude/unified dependency.
2025-11-07 10:52:50 +08:00
32 changed files with 584 additions and 706 deletions

View File

@ -3,7 +3,7 @@ import type { ReactNode } from 'react'
import SwrInitializer from '@/app/components/swr-initializer'
import { AppContextProvider } from '@/context/app-context'
import GA, { GaType } from '@/app/components/base/ga'
import AmplitudeProvider from '@/app/components/amplitude'
import AmplitudeProvider from '@/app/components/base/amplitude'
import HeaderWrapper from '@/app/components/header/header-wrapper'
import Header from '@/app/components/header'
import { EventEmitterContextProvider } from '@/context/event-emitter'

View File

@ -4,7 +4,7 @@ import Header from './header'
import SwrInitor from '@/app/components/swr-initializer'
import { AppContextProvider } from '@/context/app-context'
import GA, { GaType } from '@/app/components/base/ga'
import AmplitudeProvider from '@/app/components/amplitude'
import AmplitudeProvider from '@/app/components/base/amplitude'
import HeaderWrapper from '@/app/components/header/header-wrapper'
import { EventEmitterContextProvider } from '@/context/event-emitter'
import { ProviderContextProvider } from '@/context/provider-context'

View File

@ -49,7 +49,6 @@ import { fetchInstalledAppList } from '@/service/explore'
import { AppModeEnum } from '@/types/app'
import type { PublishWorkflowParams } from '@/types/workflow'
import { basePath } from '@/utils/var'
import { trackEvent } from '../../amplitude'
const ACCESS_MODE_MAP: Record<AccessMode, { label: string, icon: React.ElementType }> = {
[AccessMode.ORGANIZATION]: {
@ -183,12 +182,6 @@ const AppPublisher = ({
try {
await onPublish?.(params)
setPublished(true)
trackEvent('app·_published_time', {
app_mode: appDetail?.mode,
app_id: appDetail?.id,
app_name: appDetail?.name,
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
})
}
catch {
setPublished(false)

View File

@ -28,6 +28,7 @@ import Input from '@/app/components/base/input'
import { AppModeEnum } from '@/types/app'
import { DSLImportMode } from '@/models/app'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { trackEvent } from '@/app/components/base/amplitude'
type AppsProps = {
onSuccess?: () => void
@ -141,6 +142,16 @@ const Apps = ({
icon_background,
description,
})
// Track app creation from template
trackEvent('create_app_with_template', {
app_mode: mode,
template_id: currApp?.app.id,
template_name: currApp?.app.name,
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
has_description: description,
})
setIsShowCreateModal(false)
Toast.notify({
type: 'success',

View File

@ -30,6 +30,7 @@ import { getRedirection } from '@/utils/app-redirection'
import FullScreenModal from '@/app/components/base/fullscreen-modal'
import useTheme from '@/hooks/use-theme'
import { useDocLink } from '@/context/i18n'
import { trackEvent } from '@/app/components/base/amplitude'
type CreateAppProps = {
onSuccess: () => void
@ -82,6 +83,14 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate, defaultAppMode }:
icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined,
mode: appMode,
})
// Track app creation success
trackEvent('create_app', {
app_mode: appMode,
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
description,
})
notify({ type: 'success', message: t('app.newApp.appCreated') })
onSuccess()
onClose()

View File

@ -28,6 +28,7 @@ import { getRedirection } from '@/utils/app-redirection'
import cn from '@/utils/classnames'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { noop } from 'lodash-es'
import { trackEvent } from '@/app/components/base/amplitude'
type CreateFromDSLModalProps = {
show: boolean
@ -112,6 +113,13 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
return
const { id, status, app_id, app_mode, imported_dsl_version, current_dsl_version } = response
if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) {
// Track app creation from DSL import
trackEvent('create_app_with_dsl', {
app_mode,
creation_method: currentTab === CreateFromDSLModalTab.FROM_FILE ? 'dsl_file' : 'dsl_url',
has_warnings: status === DSLImportStatus.COMPLETED_WITH_WARNINGS,
})
if (onSuccess)
onSuccess()
if (onClose)

View File

@ -42,7 +42,6 @@ import { getProcessedFilesFromResponse } from '@/app/components/base/file-upload
import cn from '@/utils/classnames'
import { noop } from 'lodash-es'
import PromptLogModal from '../../base/prompt-log-modal'
import { WorkflowContextProvider } from '@/app/components/workflow/context'
type AppStoreState = ReturnType<typeof useAppStore.getState>
type ConversationListItem = ChatConversationGeneralDetail | CompletionConversationGeneralDetail
@ -780,17 +779,15 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
}
</div>
{showMessageLogModal && (
<WorkflowContextProvider>
<MessageLogModal
width={width}
currentLogItem={currentLogItem}
onCancel={() => {
setCurrentLogItem()
setShowMessageLogModal(false)
}}
defaultTab={currentLogModalActiveTab}
/>
</WorkflowContextProvider>
<MessageLogModal
width={width}
currentLogItem={currentLogItem}
onCancel={() => {
setCurrentLogItem()
setShowMessageLogModal(false)
}}
defaultTab={currentLogModalActiveTab}
/>
)}
{!isChatMode && showPromptLogModal && (
<PromptLogModal

View File

@ -8,6 +8,7 @@ import quarterOfYear from 'dayjs/plugin/quarterOfYear'
import type { QueryParam } from './index'
import Chip from '@/app/components/base/chip'
import Input from '@/app/components/base/input'
import { trackEvent } from '../../base/amplitude/utils'
dayjs.extend(quarterOfYear)
const today = dayjs()
@ -37,6 +38,9 @@ const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps)
value={queryParams.status || 'all'}
onSelect={(item) => {
setQueryParams({ ...queryParams, status: item.value as string })
trackEvent('workflow_log_filter_status_selected', {
workflow_log_filter_status: item.value as string,
})
}}
onClear={() => setQueryParams({ ...queryParams, status: 'all' })}
items={[{ value: 'all', name: 'All' },

View File

@ -1,6 +1,6 @@
import React from 'react'
import Link from 'next/link'
import { RiDiscordFill, RiDiscussLine, RiGithubFill } from '@remixicon/react'
import { RiDiscordFill, RiGithubFill } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
type CustomLinkProps = {
@ -38,9 +38,6 @@ const Footer = () => {
<CustomLink href='https://discord.gg/FngNHpbcY7'>
<RiDiscordFill className='h-5 w-5 text-text-tertiary' />
</CustomLink>
<CustomLink href='https://forum.dify.ai'>
<RiDiscussLine className='h-5 w-5 text-text-tertiary' />
</CustomLink>
</div>
</footer>
)

View File

@ -7,11 +7,12 @@ import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser'
export type IAmplitudeProps = {
apiKey?: string
enableSessionReplay?: boolean
sessionReplaySampleRate?: number
}
const AmplitudeProvider: FC<IAmplitudeProps> = ({
apiKey = '702e89332ab88a7f14e665f417244e9d',
sessionReplaySampleRate = 1,
}) => {
useEffect(() => {
// // Only enable in non-CE edition
@ -20,12 +21,7 @@ const AmplitudeProvider: FC<IAmplitudeProps> = ({
// return
// }
const sessionReplay = sessionReplayPlugin({
sampleRate: 0.1,
})
amplitude.add(sessionReplay)
// Initialize Amplitude
amplitude.init(apiKey, {
defaultTracking: {
sessions: true,
@ -33,10 +29,18 @@ const AmplitudeProvider: FC<IAmplitudeProps> = ({
formInteractions: true,
fileDownloads: true,
},
// Enable debug logs in development environment
logLevel: amplitude.Types.LogLevel.Warn,
})
}, [apiKey])
// Add Session Replay plugin
const sessionReplay = sessionReplayPlugin({
sampleRate: sessionReplaySampleRate,
})
amplitude.add(sessionReplay)
}, [apiKey, sessionReplaySampleRate])
// This is a client component that renders nothing
return null
}

View File

@ -6,7 +6,6 @@ import { useStore } from '@/app/components/app/store'
import type { WorkflowRunDetailResponse } from '@/models/log'
import type { NodeTracing, NodeTracingListResponse } from '@/types/workflow'
import { BlockEnum } from '@/app/components/workflow/types'
import { WorkflowContextProvider } from '@/app/components/workflow/context'
const SAMPLE_APP_DETAIL = {
id: 'app-demo-1',
@ -144,12 +143,10 @@ const MessageLogPreview = (props: MessageLogModalProps) => {
return (
<div className="relative min-h-[640px] w-full bg-background-default-subtle p-6">
<WorkflowContextProvider>
<MessageLogModal
{...props}
currentLogItem={mockCurrentLogItem}
/>
</WorkflowContextProvider>
<MessageLogModal
{...props}
currentLogItem={mockCurrentLogItem}
/>
</div>
)
}

View File

@ -5,7 +5,6 @@ import { useCreatePipelineDataset } from '@/service/knowledge/use-create-dataset
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
import Toast from '@/app/components/base/toast'
import { useRouter } from 'next/navigation'
import { trackEvent } from '@/app/components/amplitude'
const CreateCard = () => {
const { t } = useTranslation()
@ -23,9 +22,6 @@ const CreateCard = () => {
type: 'success',
message: t('datasetPipeline.creation.successTip'),
})
trackEvent('create_datasets_from_scratch', {
name: data.name,
})
invalidDatasetList()
push(`/datasets/${id}/pipeline`)
}

View File

@ -19,7 +19,6 @@ import Content from './content'
import Actions from './actions'
import { useCreatePipelineDatasetFromCustomized } from '@/service/knowledge/use-create-dataset'
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
import { trackEvent } from '@/app/components/amplitude'
type TemplateCardProps = {
pipeline: PipelineTemplate
@ -64,13 +63,6 @@ const TemplateCard = ({
type: 'success',
message: t('datasetPipeline.creation.successTip'),
})
trackEvent('create_datasets_with_pipeline', {
template_type: type,
template_name: pipeline.name,
chunk_structure: pipeline.chunk_structure,
})
invalidDatasetList()
if (newDataset.pipeline_id)
await handleCheckPluginDependencies(newDataset.pipeline_id, true)
@ -83,7 +75,7 @@ const TemplateCard = ({
})
},
})
}, [getPipelineTemplateInfo, createDataset, t, handleCheckPluginDependencies, push, invalidDatasetList, pipeline, type])
}, [getPipelineTemplateInfo, createDataset, t, handleCheckPluginDependencies, push, invalidDatasetList])
const handleShowTemplateDetails = useCallback(() => {
setShowDetailModal(true)

View File

@ -12,7 +12,6 @@ import Button from '@/app/components/base/button'
import { ToastContext } from '@/app/components/base/toast'
import { createEmptyDataset } from '@/service/datasets'
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
import { trackEvent } from '@/app/components/amplitude'
type IProps = {
show: boolean
@ -42,9 +41,6 @@ const EmptyDatasetCreationModal = ({
const dataset = await createEmptyDataset({ name: inputValue })
invalidDatasetList()
onHide()
trackEvent('create_empty_datasets', {
name: inputValue,
})
router.push(`/datasets/${dataset.id}/documents`)
}
catch {

View File

@ -63,7 +63,6 @@ import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/aler
import { noop } from 'lodash-es'
import { useDocLink } from '@/context/i18n'
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
import { trackEvent } from '@/app/components/amplitude'
const TextLabel: FC<PropsWithChildren> = (props) => {
return <label className='system-sm-semibold text-text-secondary'>{props.children}</label>
@ -572,12 +571,6 @@ const StepTwo = ({
updateIndexingTypeCache?.(indexType as string)
updateResultCache?.(data)
updateRetrievalMethodCache?.(retrievalConfig.search_method as string)
trackEvent('create_datasets', {
data_source_type: dataSourceType,
indexing_technique: indexType,
doc_form: currentDocForm,
})
},
},
)
@ -588,13 +581,6 @@ const StepTwo = ({
updateIndexingTypeCache?.(indexType as string)
updateResultCache?.(data)
updateRetrievalMethodCache?.(retrievalConfig.search_method as string)
// Track document addition to existing dataset
trackEvent('dataset_document_added', {
data_source_type: dataSourceType,
indexing_technique: indexType,
doc_form: currentDocForm,
})
},
})
}

View File

@ -6,7 +6,6 @@ import { useToastContext } from '@/app/components/base/toast'
import ExternalKnowledgeBaseCreate from '@/app/components/datasets/external-knowledge-base/create'
import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations'
import { createExternalKnowledgeBase } from '@/service/datasets'
import { trackEvent } from '@/app/components/amplitude'
const ExternalKnowledgeBaseConnector = () => {
const { notify } = useToastContext()
@ -18,9 +17,6 @@ const ExternalKnowledgeBaseConnector = () => {
setLoading(true)
const result = await createExternalKnowledgeBase({ body: formValue })
if (result && result.id) {
trackEvent('create_external_knowledge_base', {
value: formValue,
})
notify({ type: 'success', message: 'External Knowledge Base Connected Successfully' })
router.back()
}

View File

@ -44,7 +44,6 @@ import { AUTO_UPDATE_MODE } from '../reference-setting-modal/auto-update-setting
import { convertUTCDaySecondsToLocalSeconds, timeOfDayToDayjs } from '../reference-setting-modal/auto-update-setting/utils'
import type { PluginDetail } from '../types'
import { PluginCategoryEnum, PluginSource } from '../types'
import { trackEvent } from '../../amplitude'
const i18nPrefix = 'plugin.action'
@ -200,14 +199,6 @@ const DetailHeader = ({
const handleDelete = useCallback(async () => {
showDeleting()
const res = await uninstallPlugin(id)
trackEvent('plugin_deleted', {
plugin_id: id,
plugin_name: label[locale],
plugin_category: category,
plugin_source: source,
plugin_version: version,
})
hideDeleting()
if (res.success) {
hideDeleteConfirm()

View File

@ -30,7 +30,6 @@ import { useCategories } from '../hooks'
import { usePluginPageContext } from '../plugin-page/context'
import { PluginCategoryEnum, type PluginDetail, PluginSource } from '../types'
import Action from './action'
import { trackEvent } from '../../amplitude'
type Props = {
className?: string
@ -78,10 +77,6 @@ const PluginItem: FC<Props> = ({
}, [status, deprecated_reason])
const handleDelete = useCallback(() => {
trackEvent('plugin_deleted', {
plugin_id,
plugin_name: name,
})
refreshPluginList({ category } as any)
}, [category, refreshPluginList])

View File

@ -9,7 +9,6 @@ import { getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils'
import cn from '@/utils/classnames'
import { RiCloseLine, RiDatabase2Line, RiLoader2Line, RiPlayLargeLine } from '@remixicon/react'
import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { trackEvent } from '@/app/components/amplitude'
type RunModeProps = {
text?: string
@ -54,9 +53,6 @@ const RunMode = ({
isDisabled ? 'rounded-l-md' : 'rounded-md',
)}
onClick={() => {
trackEvent('pipeline_start_action_time', {
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
})
handleWorkflowStartRunInWorkflow()
}}
disabled={isDisabled}

View File

@ -11,7 +11,6 @@ import BlockIcon from '../../block-icon'
import cn from '@/utils/classnames'
import { useTranslation } from 'react-i18next'
import { basePath } from '@/utils/var'
import { trackEvent } from '@/app/components/amplitude'
const normalizeProviderIcon = (icon: ToolWithProvider['icon']) => {
if (typeof icon === 'string' && basePath && icon.startsWith('/') && !icon.startsWith(`${basePath}/`))
@ -68,13 +67,6 @@ const ToolItem: FC<Props> = ({
params[item.name] = ''
})
}
trackEvent('tool_selected', {
tool_name: payload.name,
tool_parameters: payload.parameters,
tool_params: params,
plugin_id: provider.plugin_id,
plugin_unique_identifier: provider.plugin_unique_identifier,
})
onSelect(BlockEnum.Tool, {
provider_id: provider.id,
provider_type: provider.type,

View File

@ -12,7 +12,6 @@ import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAnd
import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options'
import TestRunMenu, { type TestRunMenuRef, type TriggerOption, TriggerType } from './test-run-menu'
import { useToastContext } from '@/app/components/base/toast'
import { trackEvent } from '../../amplitude'
type RunModeProps = {
text?: string
@ -68,11 +67,6 @@ const RunMode = ({
return
}
trackEvent('app_start_action_time', {
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
action_type: option.type,
})
if (option.type === TriggerType.UserInput) {
handleWorkflowStartRunInWorkflow()
}

View File

@ -2,7 +2,6 @@ import { useCallback } from 'react'
import { produce } from 'immer'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { trackEvent } from '@/app/components/amplitude'
export const useWorkflowFailed = () => {
const workflowStore = useWorkflowStore()
@ -19,12 +18,6 @@ export const useWorkflowFailed = () => {
status: WorkflowRunningStatus.Failed,
}
}))
trackEvent('workflow_run_failed', {
workflow_id: workflowRunningData?.task_id,
error: workflowRunningData?.result.error,
data: workflowRunningData?.result,
})
}, [workflowStore])
return {

View File

@ -11,6 +11,7 @@ import Toast from '@/app/components/base/toast'
import { emailLoginWithCode, sendEMailLoginCode } from '@/service/common'
import I18NContext from '@/context/i18n'
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
import { trackEvent } from '@/app/components/base/amplitude'
export default function CheckCode() {
const { t, i18n } = useTranslation()
@ -43,6 +44,13 @@ export default function CheckCode() {
setIsLoading(true)
const ret = await emailLoginWithCode({ email, code, token, language })
if (ret.result === 'success') {
// Track login success event
trackEvent('user_login_success', {
method: 'email_code',
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
is_invite: !!invite_token,
})
if (invite_token) {
router.replace(`/signin/invite-settings?${searchParams.toString()}`)
}

View File

@ -12,6 +12,7 @@ import I18NContext from '@/context/i18n'
import { noop } from 'lodash-es'
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
import type { ResponseError } from '@/service/fetch'
import { trackEvent } from '@/app/components/base/amplitude'
type MailAndPasswordAuthProps = {
isInvite: boolean
@ -63,6 +64,13 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis
body: loginData,
})
if (res.result === 'success') {
// Track login success event
trackEvent('user_login_success', {
method: 'email_password',
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
is_invite: isInvite,
})
if (isInvite) {
router.replace(`/signin/invite-settings?${searchParams.toString()}`)
}

View File

@ -42,7 +42,6 @@ export default function CheckCode() {
}
setIsLoading(true)
const res = await verifyCode({ email, code, token })
console.log(res)
if ((res as MailValidityResponse).is_valid) {
const params = new URLSearchParams(searchParams)
params.set('token', encodeURIComponent((res as MailValidityResponse).token))

View File

@ -9,6 +9,7 @@ import Input from '@/app/components/base/input'
import { validPassword } from '@/config'
import type { MailRegisterResponse } from '@/service/use-common'
import { useMailRegister } from '@/service/use-common'
import { trackEvent } from '@/app/components/base/amplitude'
const ChangePasswordForm = () => {
const { t } = useTranslation()
@ -54,6 +55,12 @@ const ChangePasswordForm = () => {
})
const { result } = res as MailRegisterResponse
if (result === 'success') {
// Track registration success event
trackEvent('user_registration_success', {
method: 'email',
time: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
})
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),

View File

@ -10,8 +10,8 @@ import MaintenanceNotice from '@/app/components/header/maintenance-notice'
import { noop } from 'lodash-es'
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
import { ZENDESK_FIELD_IDS } from '@/config'
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
import { useGlobalPublicStore } from './global-public-context'
import { setUserId, setUserProperties } from '@/app/components/amplitude'
export type AppContextValue = {
userProfile: UserProfileResponse
@ -160,7 +160,9 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
}, [currentWorkspace?.id])
// #endregion Zendesk conversation fields
// #region Amplitude user tracking
useEffect(() => {
// Report user info to Amplitude when loaded
if (userProfile?.id) {
setUserId(userProfile.email)
setUserProperties({
@ -169,9 +171,10 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
has_password: userProfile.is_password_set,
})
}
}, [userProfile])
}, [userProfile?.id, userProfile?.email, userProfile?.name, userProfile?.is_password_set])
useEffect(() => {
// Report workspace info to Amplitude when loaded
if (currentWorkspace?.id && userProfile?.id) {
setUserProperties({
workspace_id: currentWorkspace.id,
@ -181,7 +184,8 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
workspace_role: currentWorkspace.role,
})
}
}, [currentWorkspace])
}, [currentWorkspace?.id, currentWorkspace?.name, currentWorkspace?.plan, currentWorkspace?.status, currentWorkspace?.role, userProfile?.id])
// #endregion Amplitude user tracking
return (
<AppContext.Provider value={{

View File

@ -1,7 +1,7 @@
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com'
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com https://api2.amplitude.com *.amplitude.com'
const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => {
// prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking

View File

@ -44,8 +44,8 @@
"knip": "knip"
},
"dependencies": {
"@amplitude/analytics-browser": "^2.30.1",
"@amplitude/plugin-session-replay-browser": "^1.23.2",
"@amplitude/analytics-browser": "^2.31.3",
"@amplitude/plugin-session-replay-browser": "^1.23.6",
"@emoji-mart/data": "^1.2.1",
"@floating-ui/react": "^0.26.28",
"@formatjs/intl-localematcher": "^0.5.10",

1075
web/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff