mirror of
https://github.com/langgenius/dify.git
synced 2026-09-21 13:20:52 +08:00
feat: daily sync (#38593)
Co-authored-by: 盐粒 Yanli <mail@yanli.one> Co-authored-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> Co-authored-by: 盐粒 Yanli <yanli@dify.ai>
This commit is contained in:
co-authored by
盐粒 Yanli
yyh
Joel
autofix-ci[bot]
QuantumGhost
yyh
盐粒 Yanli
parent
9252d81826
commit
925f97be20
@@ -76,7 +76,7 @@ describe('AgentDetailLayout', () => {
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockReplace).toHaveBeenCalledWith('/roster')
|
||||
expect(mockReplace).toHaveBeenCalledWith('/agents')
|
||||
})
|
||||
expect(screen.queryByText('Agent detail content')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import userEvent from '@testing-library/user-event'
|
||||
import { AgentDetailSection, AgentDetailTop } from '../navigation'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
pathname: '/roster/agent/agent-1/configure',
|
||||
pathname: '/agents/agent-1/configure',
|
||||
queryData: undefined as AgentAppDetailWithSite | undefined,
|
||||
}))
|
||||
|
||||
@@ -93,7 +93,7 @@ function renderAgentDetailSection(expand = true) {
|
||||
|
||||
describe('AgentDetailSection', () => {
|
||||
beforeEach(() => {
|
||||
mocks.pathname = '/roster/agent/agent-1/configure'
|
||||
mocks.pathname = '/agents/agent-1/configure'
|
||||
mocks.queryData = createAgent()
|
||||
})
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('AgentDetailTop', () => {
|
||||
render(<AgentDetailTop />)
|
||||
|
||||
expect(screen.getByRole('link', { name: 'common.mainNav.home' })).toHaveAttribute('href', '/')
|
||||
expect(screen.getByRole('link', { name: 'common.menus.roster' })).toHaveAttribute('href', '/roster')
|
||||
expect(screen.getByRole('link', { name: 'Agents' })).toHaveAttribute('href', '/agents')
|
||||
expect(screen.queryByRole('button', { name: 'common.operation.back' })).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
+55
-1
@@ -603,7 +603,16 @@ describe('AgentPreviewChat', () => {
|
||||
inputs: {},
|
||||
message: [],
|
||||
message_files: [],
|
||||
agent_thoughts: [],
|
||||
agent_thoughts: [{
|
||||
id: 'thought-with-answer',
|
||||
message_id: 'message-after-send',
|
||||
thought: '',
|
||||
answer: 'history thought answer',
|
||||
tool: '',
|
||||
tool_input: '',
|
||||
observation: '',
|
||||
position: 1,
|
||||
}],
|
||||
feedbacks: [],
|
||||
answer_tokens: 1,
|
||||
message_tokens: 1,
|
||||
@@ -635,6 +644,51 @@ describe('AgentPreviewChat', () => {
|
||||
}))).toBe(conversationMessagesResponse)
|
||||
})
|
||||
|
||||
it('should preserve historical agent thought answer when formatting chat history', async () => {
|
||||
chatMessagesGetMock.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
id: 'message-with-thought-answer',
|
||||
conversation_id: 'conversation-1',
|
||||
query: 'hello',
|
||||
answer: '',
|
||||
inputs: {},
|
||||
message: [],
|
||||
message_files: [],
|
||||
agent_thoughts: [{
|
||||
id: 'thought-with-answer',
|
||||
message_id: 'message-with-thought-answer',
|
||||
thought: '',
|
||||
answer: 'history thought answer',
|
||||
tool: '',
|
||||
tool_input: '',
|
||||
observation: '',
|
||||
position: 1,
|
||||
}],
|
||||
feedbacks: [],
|
||||
status: 'success',
|
||||
from_source: 'console',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
renderPreviewChat({
|
||||
conversationId: 'conversation-1',
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
const formattedTree = useChatMock.mock.calls.find((call) => {
|
||||
const chatTree = call[2]
|
||||
return JSON.stringify(chatTree).includes('history thought answer')
|
||||
})?.[2]
|
||||
|
||||
expect(formattedTree?.[0]?.children?.[0]?.agent_thoughts?.[0]).toEqual(expect.objectContaining({
|
||||
id: 'thought-with-answer',
|
||||
answer: 'history thought answer',
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
it('should notify the owner when a send settles with an error', async () => {
|
||||
const onSendInterrupted = vi.fn()
|
||||
renderPreviewChat({
|
||||
|
||||
@@ -261,6 +261,7 @@ const toAgentThoughtItem = (thought: AgentThought, conversationId: string): Thou
|
||||
id: thought.id,
|
||||
tool: thought.tool ?? '',
|
||||
thought: thought.thought ?? '',
|
||||
answer: thought.answer ?? '',
|
||||
tool_input: thought.tool_input ?? '',
|
||||
message_id: thought.message_id,
|
||||
conversation_id: conversationId,
|
||||
|
||||
@@ -34,7 +34,7 @@ export function AgentDetailLayout({
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldRedirectToRoster)
|
||||
router.replace('/roster')
|
||||
router.replace('/agents')
|
||||
}, [router, shouldRedirectToRoster])
|
||||
|
||||
if (shouldRedirectToRoster)
|
||||
|
||||
@@ -120,8 +120,8 @@ export function AgentDetailTop({
|
||||
<span className="shrink-0 system-md-regular text-text-quaternary">
|
||||
/
|
||||
</span>
|
||||
<Link href="/roster" className="shrink-0 truncate rounded-lg px-1.5 py-2 system-sm-semibold-uppercase text-text-secondary transition-colors hover:bg-background-default-hover hover:text-text-primary focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden">
|
||||
{tCommon('menus.roster')}
|
||||
<Link href="/agents" className="shrink-0 truncate rounded-lg px-1.5 py-2 system-sm-semibold-uppercase text-text-secondary transition-colors hover:bg-background-default-hover hover:text-text-primary focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden">
|
||||
Agents
|
||||
</Link>
|
||||
</div>
|
||||
<Tooltip>
|
||||
|
||||
@@ -3,12 +3,12 @@ import type { AgentDetailSectionKey } from './section'
|
||||
export const getAgentDetailPath = (
|
||||
agentId: string,
|
||||
section: AgentDetailSectionKey,
|
||||
) => `/roster/agent/${agentId}/${section}`
|
||||
) => `/agents/${agentId}/${section}`
|
||||
|
||||
export const getAgentIdFromPathname = (pathname: string) => {
|
||||
const [section, type, agentId] = pathname.split('/').filter(Boolean)
|
||||
const [section, agentId] = pathname.split('/').filter(Boolean)
|
||||
|
||||
if (section !== 'roster' || type !== 'agent')
|
||||
if (section !== 'agents')
|
||||
return undefined
|
||||
|
||||
return agentId
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('CreateAgentDialog', () => {
|
||||
})
|
||||
|
||||
expect(toastMock.success).toHaveBeenCalledWith('agentV2.roster.createSuccess')
|
||||
expect(routerPushMock).toHaveBeenCalledWith('/roster/agent/agent-1/configure')
|
||||
expect(routerPushMock).toHaveBeenCalledWith('/agents/agent-1/configure')
|
||||
})
|
||||
|
||||
it('shows a field error when creating with an empty name', async () => {
|
||||
|
||||
@@ -130,7 +130,7 @@ function AgentRosterItem({
|
||||
<article className="group relative col-span-1 h-36.5 min-w-0 overflow-hidden rounded-xl border-[0.5px] border-solid border-components-card-border bg-components-card-bg shadow-xs shadow-shadow-shadow-3 transition-shadow duration-200 ease-in-out after:pointer-events-none after:absolute after:inset-0 after:rounded-xl after:content-[''] hover:shadow-lg has-[>div>a:focus-visible]:after:inset-ring-2 has-[>div>a:focus-visible]:after:inset-ring-state-accent-solid">
|
||||
<div className="flex h-full min-w-0 flex-col">
|
||||
<Link
|
||||
href={`/roster/agent/${agent.id}/configure`}
|
||||
href={`/agents/${agent.id}/configure`}
|
||||
aria-labelledby={nameId}
|
||||
aria-describedby={agent.description ? descriptionId : undefined}
|
||||
className="block shrink-0 cursor-pointer touch-manipulation outline-hidden"
|
||||
|
||||
@@ -43,7 +43,6 @@ const getFilteredRosterItems = (
|
||||
|
||||
export default function RosterPage() {
|
||||
const { t } = useTranslation('agentV2')
|
||||
const { t: tCommon } = useTranslation('common')
|
||||
const [keyword] = useQueryState(rosterQueryParamNames.keyword, rosterKeywordQueryParser)
|
||||
const [rosterFilter] = useQueryState(rosterQueryParamNames.filter, rosterFilterQueryParser)
|
||||
const [createdByMe] = useQueryState(rosterQueryParamNames.createdByMe, rosterCreatedByMeQueryParser)
|
||||
@@ -84,14 +83,14 @@ export default function RosterPage() {
|
||||
const draftAgents = Math.max(rosterItems.length - publishedAgents, 0)
|
||||
const filteredRosterItems = getFilteredRosterItems(rosterItems, rosterFilter)
|
||||
|
||||
useDocumentTitle(tCommon('menus.roster'))
|
||||
useDocumentTitle('Agents')
|
||||
|
||||
return (
|
||||
<div className="flex h-0 min-w-0 grow flex-col overflow-hidden bg-background-body">
|
||||
<div className="shrink-0 bg-background-body px-8 pt-4 pb-2">
|
||||
<div className="flex h-6 min-w-0 items-center justify-between gap-4">
|
||||
<h1 className="min-w-0 flex-1 truncate text-[18px]/[21.6px] font-semibold text-text-primary">
|
||||
{tCommon('menus.roster')}
|
||||
Agents
|
||||
</h1>
|
||||
<a
|
||||
href="https://docs.dify.ai/"
|
||||
|
||||
Reference in New Issue
Block a user