This commit is contained in:
zxhlyh 2025-03-06 16:47:26 +08:00
commit 30f0b66f01
29 changed files with 193 additions and 54 deletions

View File

@ -522,6 +522,7 @@ class WorkflowBasedAppRunner(AppRunner):
status=event.status,
data=event.data,
metadata=event.metadata,
node_id=event.node_id,
)
)
elif isinstance(event, ParallelBranchRunStartedEvent):

View File

@ -427,6 +427,7 @@ class QueueAgentLogEvent(AppQueueEvent):
status: str
data: Mapping[str, Any]
metadata: Optional[Mapping[str, Any]] = None
node_id: str
class QueueNodeRetryEvent(QueueNodeStartedEvent):

View File

@ -817,6 +817,7 @@ class AgentLogStreamResponse(StreamResponse):
status: str
data: Mapping[str, Any]
metadata: Optional[Mapping[str, Any]] = None
node_id: str
event: StreamEvent = StreamEvent.AGENT_LOG
data: Data

View File

@ -959,5 +959,6 @@ class WorkflowCycleManage:
status=event.status,
data=event.data,
metadata=event.metadata,
node_id=event.node_id,
),
)

View File

@ -268,6 +268,7 @@ class AgentLogEvent(BaseAgentEvent):
status: str = Field(..., description="status")
data: Mapping[str, Any] = Field(..., description="data")
metadata: Optional[Mapping[str, Any]] = Field(default=None, description="metadata")
node_id: str = Field(..., description="agent node id")
InNodeEvent = BaseNodeEvent | BaseParallelBranchEvent | BaseIterationEvent | BaseAgentEvent | BaseLoopEvent

View File

@ -18,6 +18,7 @@ from core.workflow.entities.node_entities import AgentNodeStrategyInit, NodeRunM
from core.workflow.entities.variable_pool import VariablePool, VariableValue
from core.workflow.graph_engine.condition_handlers.condition_manager import ConditionManager
from core.workflow.graph_engine.entities.event import (
BaseAgentEvent,
BaseIterationEvent,
BaseLoopEvent,
GraphEngineEvent,
@ -502,7 +503,7 @@ class GraphEngine:
break
yield event
if event.parallel_id == parallel_id:
if not isinstance(event, BaseAgentEvent) and event.parallel_id == parallel_id:
if isinstance(event, ParallelBranchRunSucceededEvent):
succeeded_count += 1
if succeeded_count == len(futures):

View File

@ -338,6 +338,7 @@ class ToolNode(BaseNode[ToolNodeData]):
data=message.message.data,
label=message.message.label,
metadata=message.message.metadata,
node_id=self.node_id,
)
# check if the agent log is already in the list

View File

@ -153,7 +153,8 @@ services:
- "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}"
volumes:
- ./volumes/plugin_daemon:/app/storage
depends_on:
- db
# ssrf_proxy server
# for more information, please refer to

View File

@ -568,7 +568,8 @@ services:
- "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}"
volumes:
- ./volumes/plugin_daemon:/app/storage
depends_on:
- db
# ssrf_proxy server
# for more information, please refer to

View File

@ -1543,6 +1543,59 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/metadata'
method='POST'
title='Create a Knowledge metadata'
name='#create_metadata'
/>
<Row>
<Col>
### POST
<Properties>
<Property name='dataset_id' type='string' key='dataset_id'>
Knowledge ID
</Property>
</Properties>
### Request Body
<Properties>
<Property name='segment' type='object' key='segment'>
- <code>type</code> (string) metadata type, required
- <code>name</code> (string) metadata name, required
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup
title="Request"
tag="POST"
label="/datasets/{dataset_id}/metadata"
targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{\"segment\": {\"content\": \"1\",\"answer\": \"1\", \"keywords\": [\"a\"], \"enabled\": false}}'`}
>
```bash {{ title: 'cURL' }}
curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
--header 'Content-Type: application/json' \
--data-raw '{
"segment": {
"content": "1",
"answer": "1",
}
}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"doc_form": "text_model"
}
```
</CodeGroup>
</Col>
</Row>
<hr className='ml-0 mr-0' />
<Row>
<Col>
### Error message

View File

@ -1,7 +1,7 @@
import React, { type FC } from 'react'
import type { CalendarItemProps } from '../types'
import cn from '@/utils/classnames'
import dayjs from 'dayjs'
import dayjs from '../utils/dayjs'
const Item: FC<CalendarItemProps> = ({
day,

View File

@ -22,18 +22,18 @@ const Header: FC<DatePickerHeaderProps> = ({
<RiArrowDownSLine className='w-4 h-4 text-text-tertiary' />
</button>
</div>
<button
onClick={onClickNextMonth}
className='p-1.5 hover:bg-state-base-hover rounded-lg'
>
<RiArrowDownSLine className='w-[18px] h-[18px] text-text-secondary' />
</button>
<button
onClick={onClickPrevMonth}
className='p-1.5 hover:bg-state-base-hover rounded-lg'
>
<RiArrowUpSLine className='w-[18px] h-[18px] text-text-secondary' />
</button>
<button
onClick={onClickNextMonth}
className='p-1.5 hover:bg-state-base-hover rounded-lg'
>
<RiArrowDownSLine className='w-[18px] h-[18px] text-text-secondary' />
</button>
</div>
)
}

View File

@ -1,10 +1,16 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import dayjs, { type Dayjs } from 'dayjs'
import { RiCalendarLine, RiCloseCircleFill } from '@remixicon/react'
import cn from '@/utils/classnames'
import type { DatePickerProps, Period } from '../types'
import { ViewType } from '../types'
import { cloneTime, getDaysInMonth, getHourIn12Hour } from '../utils'
import type { Dayjs } from 'dayjs'
import dayjs, {
clearMonthMapCache,
cloneTime,
getDateWithTimezone,
getDaysInMonth,
getHourIn12Hour,
} from '../utils/dayjs'
import {
PortalToFollowElem,
PortalToFollowElemContent,
@ -23,6 +29,7 @@ import { useAppContext } from '@/context/app-context'
const DatePicker = ({
value,
timezone,
onChange,
onClear,
placeholder,
@ -37,12 +44,15 @@ const DatePicker = ({
const [isOpen, setIsOpen] = useState(false)
const [view, setView] = useState(ViewType.date)
const containerRef = useRef<HTMLDivElement>(null)
const isInitial = useRef(true)
const inputValue = useRef(value ? value.tz(timezone) : undefined).current
const defaultValue = useRef(getDateWithTimezone({ timezone })).current
const [currentDate, setCurrentDate] = useState(value || dayjs())
const [selectedDate, setSelectedDate] = useState(value)
const [currentDate, setCurrentDate] = useState(inputValue || defaultValue)
const [selectedDate, setSelectedDate] = useState(inputValue)
const [selectedMonth, setSelectedMonth] = useState((value || dayjs()).month())
const [selectedYear, setSelectedYear] = useState((value || dayjs()).year())
const [selectedMonth, setSelectedMonth] = useState((inputValue || defaultValue).month())
const [selectedYear, setSelectedYear] = useState((inputValue || defaultValue).year())
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
@ -55,6 +65,25 @@ const DatePicker = ({
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
useEffect(() => {
if (isInitial.current) {
isInitial.current = false
return
}
clearMonthMapCache()
if (value) {
const newValue = getDateWithTimezone({ date: value, timezone })
setCurrentDate(newValue)
setSelectedDate(newValue)
onChange(newValue)
}
else {
setCurrentDate(prev => getDateWithTimezone({ date: prev, timezone }))
setSelectedDate(prev => prev ? getDateWithTimezone({ date: prev, timezone }) : undefined)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [timezone])
const handleClickTrigger = (e: React.MouseEvent) => {
e.stopPropagation()
if (isOpen) {
@ -63,15 +92,15 @@ const DatePicker = ({
}
setView(ViewType.date)
setIsOpen(true)
if (value) {
setCurrentDate(value)
setSelectedDate(value)
}
}
const handleClear = (e: React.MouseEvent) => {
const newDate = dayjs()
e.stopPropagation()
setSelectedDate(undefined)
setCurrentDate(prev => prev || newDate)
setSelectedMonth(prev => prev || newDate.month())
setSelectedYear(prev => prev || newDate.year())
if (!isOpen)
onClear()
}
@ -89,13 +118,13 @@ const DatePicker = ({
}, [currentDate])
const handleDateSelect = useCallback((day: Dayjs) => {
const newDate = cloneTime(day, selectedDate || dayjs())
const newDate = cloneTime(day, selectedDate || getDateWithTimezone({ timezone }))
setCurrentDate(newDate)
setSelectedDate(newDate)
}, [selectedDate])
}, [selectedDate, timezone])
const handleSelectCurrentDate = () => {
const newDate = dayjs()
const newDate = getDateWithTimezone({ timezone })
setCurrentDate(newDate)
setSelectedDate(newDate)
onChange(newDate)
@ -126,19 +155,19 @@ const DatePicker = ({
}
const handleSelectHour = useCallback((hour: string) => {
const selectedTime = selectedDate || dayjs()
const selectedTime = selectedDate || getDateWithTimezone({ timezone })
handleTimeSelect(hour, selectedTime.minute().toString().padStart(2, '0'), selectedTime.format('A') as Period)
}, [selectedDate])
}, [selectedDate, timezone])
const handleSelectMinute = useCallback((minute: string) => {
const selectedTime = selectedDate || dayjs()
const selectedTime = selectedDate || getDateWithTimezone({ timezone })
handleTimeSelect(getHourIn12Hour(selectedTime).toString().padStart(2, '0'), minute, selectedTime.format('A') as Period)
}, [selectedDate])
}, [selectedDate, timezone])
const handleSelectPeriod = useCallback((period: Period) => {
const selectedTime = selectedDate || dayjs()
const selectedTime = selectedDate || getDateWithTimezone({ timezone })
handleTimeSelect(getHourIn12Hour(selectedTime).toString().padStart(2, '0'), selectedTime.minute().toString().padStart(2, '0'), period)
}, [selectedDate])
}, [selectedDate, timezone])
const handleOpenYearMonthPicker = () => {
setSelectedMonth(currentDate.month())
@ -163,15 +192,13 @@ const DatePicker = ({
}, [])
const handleYearMonthConfirm = () => {
setCurrentDate((prev) => {
return prev ? prev.clone().month(selectedMonth).year(selectedYear) : dayjs().month(selectedMonth).year(selectedYear)
})
setCurrentDate(prev => prev.clone().month(selectedMonth).year(selectedYear))
setView(ViewType.date)
}
const timeFormat = needTimePicker ? 'MMMM D, YYYY hh:mm A' : 'MMMM D, YYYY'
const displayValue = value?.format(timeFormat) || ''
const displayTime = (selectedDate || dayjs().startOf('day')).format('hh:mm A')
const displayTime = selectedDate?.format('hh:mm A') || '--:-- --'
const placeholderDate = isOpen && selectedDate ? selectedDate.format(timeFormat) : (placeholder || t('time.defaultPlaceholder'))
return (

View File

@ -1,4 +1,4 @@
import dayjs from 'dayjs'
import dayjs from './utils/dayjs'
import { Period } from './types'
import { useTranslation } from 'react-i18next'

View File

@ -1,7 +1,6 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'
import dayjs from 'dayjs'
import type { Period, TimePickerProps } from '../types'
import { cloneTime, getHourIn12Hour } from '../utils'
import dayjs, { cloneTime, getDateWithTimezone, getHourIn12Hour } from '../utils/dayjs'
import {
PortalToFollowElem,
PortalToFollowElemContent,
@ -16,6 +15,7 @@ import cn from '@/utils/classnames'
const TimePicker = ({
value,
timezone,
placeholder,
onChange,
onClear,
@ -24,7 +24,8 @@ const TimePicker = ({
const { t } = useTranslation()
const [isOpen, setIsOpen] = useState(false)
const containerRef = useRef<HTMLDivElement>(null)
const [selectedTime, setSelectedTime] = useState(value)
const isInitial = useRef(true)
const [selectedTime, setSelectedTime] = useState(value ? getDateWithTimezone({ timezone, date: value }) : undefined)
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
@ -35,6 +36,22 @@ const TimePicker = ({
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
useEffect(() => {
if (isInitial.current) {
isInitial.current = false
return
}
if (value) {
const newValue = getDateWithTimezone({ date: value, timezone })
setSelectedTime(newValue)
onChange(newValue)
}
else {
setSelectedTime(prev => prev ? getDateWithTimezone({ date: prev, timezone }) : undefined)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [timezone])
const handleClickTrigger = (e: React.MouseEvent) => {
e.stopPropagation()
if (isOpen) {
@ -42,6 +59,8 @@ const TimePicker = ({
return
}
setIsOpen(true)
if (value)
setSelectedTime(value)
}
const handleClear = (e: React.MouseEvent) => {
@ -74,11 +93,11 @@ const TimePicker = ({
}, [selectedTime])
const handleSelectCurrentTime = useCallback(() => {
const newDate = dayjs()
const newDate = getDateWithTimezone({ timezone })
setSelectedTime(newDate)
onChange(newDate)
setIsOpen(false)
}, [onChange])
}, [onChange, timezone])
const handleConfirm = useCallback(() => {
onChange(selectedTime)

View File

@ -21,6 +21,7 @@ export type TriggerProps = {
export type DatePickerProps = {
value: Dayjs | undefined
timezone?: string
placeholder?: string
needTimePicker?: boolean
onChange: (date: Dayjs | undefined) => void
@ -48,6 +49,7 @@ export type DatePickerFooterProps = {
export type TimePickerProps = {
value: Dayjs | undefined
timezone?: string
placeholder?: string
onChange: (date: Dayjs | undefined) => void
onClear: () => void

View File

@ -1,5 +1,12 @@
import type { Dayjs } from 'dayjs'
import type { Day } from './types'
import dayjs, { type Dayjs } from 'dayjs'
import type { Day } from '../types'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc)
dayjs.extend(timezone)
export default dayjs
const monthMaps: Record<string, Day[]> = {}
@ -58,7 +65,16 @@ export const getDaysInMonth = (currentDate: Dayjs) => {
return days
}
export const clearMonthMapCache = () => {
for (const key in monthMaps)
delete monthMaps[key]
}
export const getHourIn12Hour = (date: Dayjs) => {
const hour = date.hour()
return hour === 0 ? 12 : hour >= 12 ? hour - 12 : hour
}
export const getDateWithTimezone = (props: { date?: Dayjs, timezone?: string }) => {
return props.date ? dayjs.tz(props.date, props.timezone) : dayjs().tz(props.timezone)
}

View File

@ -239,7 +239,7 @@ const Link = ({ node, ...props }: any) => {
}
}
export function Markdown(props: { content: string; className?: string }) {
export function Markdown(props: { content: string; className?: string; customDisallowedElements?: string[] }) {
const latexContent = flow([
preprocessThinkTag,
preprocessLaTeX,
@ -274,7 +274,7 @@ export function Markdown(props: { content: string; className?: string }) {
}
},
]}
disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', 'input']}
disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]}
components={{
code: CodeBlock,
img: Img,

View File

@ -22,7 +22,6 @@ type Props = {
}
const KeyValue = ({ icon, label, tooltip }: { icon: ReactNode; label: string; tooltip?: ReactNode }) => {
console.log(label)
return (
<div className='flex text-text-tertiary'>
<div className='size-4 flex items-center justify-center'>

View File

@ -181,6 +181,7 @@ const ChunkContent: FC<IChunkContentProps> = ({
<Markdown
className='h-full w-full !text-text-secondary'
content={question}
customDisallowedElements={['input']}
/>
)
}

View File

@ -50,6 +50,7 @@ const ChunkContent: FC<ChunkContentProps> = ({
className,
)}
content={sign_content || content || ''}
customDisallowedElements={['input']}
/>
}

View File

@ -60,6 +60,7 @@ const ChunkDetailModal: FC<Props> = ({
<Markdown
className={cn('!mt-2 !text-text-secondary', heighClassName)}
content={sign_content || content}
customDisallowedElements={['input']}
/>
{!isParentChildRetrieval && keywords && keywords.length > 0 && (
<div className='mt-6'>

View File

@ -47,7 +47,11 @@ const ResultItem: FC<Props> = ({
{/* Main */}
<div className='mt-1 px-3'>
<Markdown className='line-clamp-2' content={sign_content || content} />
<Markdown
className='line-clamp-2'
content={sign_content || content}
customDisallowedElements={['input']}
/>
{isParentChildRetrieval && (
<div className='mt-1'>
<div

View File

@ -9,7 +9,6 @@ import cn from '@/utils/classnames'
import type { TriggerProps } from '@/app/components/base/date-and-time-picker/types'
import useTimestamp from '@/hooks/use-timestamp'
import { useTranslation } from 'react-i18next'
import { useAppContext } from '@/context/app-context'
type Props = {
className?: string
@ -22,12 +21,12 @@ const WrappedDatePicker = ({
onChange,
}: Props) => {
const { t } = useTranslation()
const { userProfile: { timezone } } = useAppContext()
// const { userProfile: { timezone } } = useAppContext()
const { formatTime: formatTimestamp } = useTimestamp()
const handleDateChange = useCallback((date?: dayjs.Dayjs) => {
if (date)
onChange(date.valueOf())
onChange(date.unix())
else
onChange(null)
}, [onChange])
@ -43,7 +42,7 @@ const WrappedDatePicker = ({
value ? 'text-text-secondary' : 'text-text-tertiary',
)}
>
{value ? formatTimestamp(dayjs.utc(value).tz(timezone).valueOf() / 1000, t('datasetDocuments.metadata.dateTimeFormat')) : 'Choose a time...'}
{value ? formatTimestamp(value, t('datasetDocuments.metadata.dateTimeFormat')) : t('dataset.metadata.chooseTime')}
</div>
<RiCloseCircleFill
className={cn(
@ -60,11 +59,11 @@ const WrappedDatePicker = ({
/>
</div>
)
}, [className, value, handleDateChange])
}, [className, value, formatTimestamp, t, handleDateChange])
return (
<DatePicker
value={dayjs(value || Date.now())}
value={dayjs(value ? value * 1000 : Date.now())}
onChange={handleDateChange}
onClear={handleDateChange}
renderTrigger={renderTrigger}

View File

@ -2,7 +2,7 @@
import type { FC } from 'react'
import React from 'react'
import { useRouter } from 'next/navigation'
import { type MetadataItemWithValue, isShowManageMetadataLocalStorageKey } from '../types'
import { DataType, type MetadataItemWithValue, isShowManageMetadataLocalStorageKey } from '../types'
import Field from './field'
import InputCombined from '../edit-metadata-batch/input-combined'
import { RiDeleteBinLine, RiQuestionLine } from '@remixicon/react'
@ -11,6 +11,8 @@ import cn from '@/utils/classnames'
import Divider from '@/app/components/base/divider'
import SelectMetadataModal from '../metadata-dataset/select-metadata-modal'
import AddMetadataButton from '../add-metadata-button'
import useTimestamp from '@/hooks/use-timestamp'
import { useTranslation } from 'react-i18next'
type Props = {
dataSetId: string
@ -46,6 +48,9 @@ const InfoGroup: FC<Props> = ({
onAdd,
}) => {
const router = useRouter()
const { t } = useTranslation()
const { formatTime: formatTimestamp } = useTimestamp()
const handleMangeMetadata = () => {
localStorage.setItem(isShowManageMetadataLocalStorageKey, 'true')
router.push(`/datasets/${dataSetId}/documents`)
@ -96,7 +101,7 @@ const InfoGroup: FC<Props> = ({
<RiDeleteBinLine className='size-4' onClick={() => onDelete?.(item)} />
</div>
</div>
) : (<div className='py-1 system-xs-regular text-text-secondary'>{item.value}</div>)}
) : (<div className='py-1 system-xs-regular text-text-secondary'>{(item.value && item.type === DataType.time) ? formatTimestamp((item.value as number), t('datasetDocuments.metadata.dateTimeFormat')) : item.value}</div>)}
</Field>
))}
</div>

View File

@ -73,6 +73,7 @@ const ConditionDate = ({
return (
<div className='px-2 py-1 h-8'>
<DatePicker
timezone={timezone}
value={value ? dayjs(value * 1000) : undefined}
onChange={handleDateChange}
onClear={handleDateChange}

View File

@ -127,7 +127,7 @@ const ConditionItem = ({
<div className='mx-1 w-[1px] h-3 bg-divider-regular'></div>
<ConditionOperator
disabled={!canChooseOperator}
variableType={currentMetadata!.type}
variableType={currentMetadata?.type || MetadataFilteringVariableType.string}
value={condition.comparison_operator}
onSelect={handleConditionOperatorChange}
/>

View File

@ -171,6 +171,7 @@ const translation = {
metadata: {
metadata: 'Metadata',
addMetadata: 'Add Metadata',
chooseTime: 'Choose a time...',
createMetadata: {
title: 'New Metadata',
back: 'Back',

View File

@ -171,6 +171,7 @@ const translation = {
metadata: {
metadata: '元数据',
addMetadata: '添加元数据',
chooseTime: '选择时间',
createMetadata: {
title: '新建元数据',
back: '返回',