Introduce Plugins (#13836)

Signed-off-by: yihong0618 <zouzou0208@gmail.com>
Signed-off-by: -LAN- <laipz8200@outlook.com>
Signed-off-by: xhe <xw897002528@gmail.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: takatost <takatost@gmail.com>
Co-authored-by: kurokobo <kuro664@gmail.com>
Co-authored-by: Novice Lee <novicelee@NoviPro.local>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: AkaraChen <akarachen@outlook.com>
Co-authored-by: Yi <yxiaoisme@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: JzoNg <jzongcode@gmail.com>
Co-authored-by: twwu <twwu@dify.ai>
Co-authored-by: Hiroshi Fujita <fujita-h@users.noreply.github.com>
Co-authored-by: AkaraChen <85140972+AkaraChen@users.noreply.github.com>
Co-authored-by: NFish <douxc512@gmail.com>
Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com>
Co-authored-by: 非法操作 <hjlarry@163.com>
Co-authored-by: Novice <857526207@qq.com>
Co-authored-by: Hiroki Nagai <82458324+nagaihiroki-git@users.noreply.github.com>
Co-authored-by: Gen Sato <52241300+halogen22@users.noreply.github.com>
Co-authored-by: eux <euxuuu@gmail.com>
Co-authored-by: huangzhuo1949 <167434202+huangzhuo1949@users.noreply.github.com>
Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com>
Co-authored-by: lotsik <lotsik@mail.ru>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: nite-knite <nkCoding@gmail.com>
Co-authored-by: Jyong <76649700+JohnJyong@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: gakkiyomi <gakkiyomi@aliyun.com>
Co-authored-by: CN-P5 <heibai2006@gmail.com>
Co-authored-by: CN-P5 <heibai2006@qq.com>
Co-authored-by: Chuehnone <1897025+chuehnone@users.noreply.github.com>
Co-authored-by: yihong <zouzou0208@gmail.com>
Co-authored-by: Kevin9703 <51311316+Kevin9703@users.noreply.github.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: Boris Feld <lothiraldan@gmail.com>
Co-authored-by: mbo <himabo@gmail.com>
Co-authored-by: mabo <mabo@aeyes.ai>
Co-authored-by: Warren Chen <warren.chen830@gmail.com>
Co-authored-by: JzoNgKVO <27049666+JzoNgKVO@users.noreply.github.com>
Co-authored-by: jiandanfeng <chenjh3@wangsu.com>
Co-authored-by: zhu-an <70234959+xhdd123321@users.noreply.github.com>
Co-authored-by: zhaoqingyu.1075 <zhaoqingyu.1075@bytedance.com>
Co-authored-by: 海狸大師 <86974027+yenslife@users.noreply.github.com>
Co-authored-by: Xu Song <xusong.vip@gmail.com>
Co-authored-by: rayshaw001 <396301947@163.com>
Co-authored-by: Ding Jiatong <dingjiatong@gmail.com>
Co-authored-by: Bowen Liang <liangbowen@gf.com.cn>
Co-authored-by: JasonVV <jasonwangiii@outlook.com>
Co-authored-by: le0zh <newlight@qq.com>
Co-authored-by: zhuxinliang <zhuxinliang@didiglobal.com>
Co-authored-by: k-zaku <zaku99@outlook.jp>
Co-authored-by: luckylhb90 <luckylhb90@gmail.com>
Co-authored-by: hobo.l <hobo.l@binance.com>
Co-authored-by: jiangbo721 <365065261@qq.com>
Co-authored-by: 刘江波 <jiangbo721@163.com>
Co-authored-by: Shun Miyazawa <34241526+miya@users.noreply.github.com>
Co-authored-by: EricPan <30651140+Egfly@users.noreply.github.com>
Co-authored-by: crazywoola <427733928@qq.com>
Co-authored-by: sino <sino2322@gmail.com>
Co-authored-by: Jhvcc <37662342+Jhvcc@users.noreply.github.com>
Co-authored-by: lowell <lowell.hu@zkteco.in>
Co-authored-by: Boris Polonsky <BorisPolonsky@users.noreply.github.com>
Co-authored-by: Ademílson Tonato <ademilsonft@outlook.com>
Co-authored-by: Ademílson Tonato <ademilson.tonato@refurbed.com>
Co-authored-by: IWAI, Masaharu <iwaim.sub@gmail.com>
Co-authored-by: Yueh-Po Peng (Yabi) <94939112+y10ab1@users.noreply.github.com>
Co-authored-by: Jason <ggbbddjm@gmail.com>
Co-authored-by: Xin Zhang <sjhpzx@gmail.com>
Co-authored-by: yjc980121 <3898524+yjc980121@users.noreply.github.com>
Co-authored-by: heyszt <36215648+hieheihei@users.noreply.github.com>
Co-authored-by: Abdullah AlOsaimi <osaimiacc@gmail.com>
Co-authored-by: Abdullah AlOsaimi <189027247+osaimi@users.noreply.github.com>
Co-authored-by: Yingchun Lai <laiyingchun@apache.org>
Co-authored-by: Hash Brown <hi@xzd.me>
Co-authored-by: zuodongxu <192560071+zuodongxu@users.noreply.github.com>
Co-authored-by: Masashi Tomooka <tmokmss@users.noreply.github.com>
Co-authored-by: aplio <ryo.091219@gmail.com>
Co-authored-by: Obada Khalili <54270856+obadakhalili@users.noreply.github.com>
Co-authored-by: Nam Vu <zuzoovn@gmail.com>
Co-authored-by: Kei YAMAZAKI <1715090+kei-yamazaki@users.noreply.github.com>
Co-authored-by: TechnoHouse <13776377+deephbz@users.noreply.github.com>
Co-authored-by: Riddhimaan-Senapati <114703025+Riddhimaan-Senapati@users.noreply.github.com>
Co-authored-by: MaFee921 <31881301+2284730142@users.noreply.github.com>
Co-authored-by: te-chan <t-nakanome@sakura-is.co.jp>
Co-authored-by: HQidea <HQidea@users.noreply.github.com>
Co-authored-by: Joshbly <36315710+Joshbly@users.noreply.github.com>
Co-authored-by: xhe <xw897002528@gmail.com>
Co-authored-by: weiwenyan-dev <154779315+weiwenyan-dev@users.noreply.github.com>
Co-authored-by: ex_wenyan.wei <ex_wenyan.wei@tcl.com>
Co-authored-by: engchina <12236799+engchina@users.noreply.github.com>
Co-authored-by: engchina <atjapan2015@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 呆萌闷油瓶 <253605712@qq.com>
Co-authored-by: Kemal <kemalmeler@outlook.com>
Co-authored-by: Lazy_Frog <4590648+lazyFrogLOL@users.noreply.github.com>
Co-authored-by: Yi Xiao <54782454+YIXIAO0@users.noreply.github.com>
Co-authored-by: Steven sun <98230804+Tuyohai@users.noreply.github.com>
Co-authored-by: steven <sunzwj@digitalchina.com>
Co-authored-by: Kalo Chin <91766386+fdb02983rhy@users.noreply.github.com>
Co-authored-by: Katy Tao <34019945+KatyTao@users.noreply.github.com>
Co-authored-by: depy <42985524+h4ckdepy@users.noreply.github.com>
Co-authored-by: 胡春东 <gycm520@gmail.com>
Co-authored-by: Junjie.M <118170653@qq.com>
Co-authored-by: MuYu <mr.muzea@gmail.com>
Co-authored-by: Naoki Takashima <39912547+takatea@users.noreply.github.com>
Co-authored-by: Summer-Gu <37869445+gubinjie@users.noreply.github.com>
Co-authored-by: Fei He <droxer.he@gmail.com>
Co-authored-by: ybalbert001 <120714773+ybalbert001@users.noreply.github.com>
Co-authored-by: Yuanbo Li <ybalbert@amazon.com>
Co-authored-by: douxc <7553076+douxc@users.noreply.github.com>
Co-authored-by: liuzhenghua <1090179900@qq.com>
Co-authored-by: Wu Jiayang <62842862+Wu-Jiayang@users.noreply.github.com>
Co-authored-by: Your Name <you@example.com>
Co-authored-by: kimjion <45935338+kimjion@users.noreply.github.com>
Co-authored-by: AugNSo <song.tiankai@icloud.com>
Co-authored-by: llinvokerl <38915183+llinvokerl@users.noreply.github.com>
Co-authored-by: liusurong.lsr <liusurong.lsr@alibaba-inc.com>
Co-authored-by: Vasu Negi <vasu-negi@users.noreply.github.com>
Co-authored-by: Hundredwz <1808096180@qq.com>
Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
This commit is contained in:
Yeuoly 2025-02-17 17:05:13 +08:00 • committed by GitHub
commit 403e2d58b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3272 changed files with 66339 additions and 281594 deletions

View file

@ -10,6 +10,10 @@ NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
# console or api domain.
# example: http://udify.app/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
# The APIFREX for MARKETPLACE
NEXT_PUBLIC_MARKETPLACE_API_PREFIX=http://localhost:5002/api
# The URL for MARKETPLACE
NEXT_PUBLIC_MARKETPLACE_URL_PREFIX=
# SENTRY
NEXT_PUBLIC_SENTRY_DSN=
@ -26,6 +30,8 @@ NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=60000
# CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
NEXT_PUBLIC_CSP_WHITELIST=
# Github Access Token, used for invoking Github API
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=
# The maximum number of top-k value for RAG.
NEXT_PUBLIC_TOP_K_MAX_VALUE=10

View file

@ -1,7 +0,0 @@
/**/node_modules/*
node_modules/
dist/
build/
out/
.next/

View file

@ -1,31 +0,0 @@
{
"extends": [
"next",
"@antfu",
"plugin:storybook/recommended"
],
"rules": {
"@typescript-eslint/consistent-type-definitions": [
"error",
"type"
],
"@typescript-eslint/no-var-requires": "off",
"no-console": "off",
"indent": "off",
"@typescript-eslint/indent": [
"error",
2,
{
"SwitchCase": 1,
"flatTernaryExpressions": false,
"ignoredNodes": [
"PropertyDefinition[decorators]",
"TSUnionType",
"FunctionExpression[params]:has(Identifier[decorators])"
]
}
],
"react-hooks/exhaustive-deps": "warn",
"react/display-name": "warn"
}
}

7
web/.gitignore vendored
View file

@ -44,12 +44,11 @@ package-lock.json
.pnp.cjs
.pnp.loader.mjs
.yarn/
.yarnrc.yml
# pmpm
pnpm-lock.yaml
.favorites.json
# storybook
/storybook-static
*storybook.log
# mise

View file

@ -1,6 +1,3 @@
#!/usr/bin/env bash
. "$(dirname -- "$0")/_/husky.sh"
# get the list of modified files
files=$(git diff --cached --name-only)
@ -50,7 +47,7 @@ fi
if $web_modified; then
echo "Running ESLint on web module"
cd ./web || exit 1
npx lint-staged
lint-staged
echo "Running unit tests check"
modified_files=$(git diff --cached --name-only -- utils | grep -v '\.spec\.ts$' || true)
@ -63,7 +60,7 @@ if $web_modified; then
# check if the test file exists
if [ -f "../$test_file" ]; then
echo "Detected changes in $file, running corresponding unit tests..."
npm run test "../$test_file"
pnpm run test "../$test_file"
if [ $? -ne 0 ]; then
echo "Unit tests failed. Please fix the errors before committing."

View file

@ -1,19 +1,19 @@
import type { StorybookConfig } from '@storybook/nextjs'
const config: StorybookConfig = {
// stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
stories: ['../app/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/nextjs',
options: {},
},
staticDirs: ['../public'],
// stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
stories: ['../app/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/nextjs',
options: {},
},
staticDirs: ['../public'],
}
export default config

View file

@ -1,6 +1,6 @@
import React from 'react'
import type { Preview } from '@storybook/react'
import { withThemeByDataAttribute } from '@storybook/addon-themes';
import { withThemeByDataAttribute } from '@storybook/addon-themes'
import I18nServer from '../app/components/i18n-server'
import '../app/styles/globals.css'
@ -8,30 +8,30 @@ import '../app/styles/markdown.scss'
import './storybook.css'
export const decorators = [
withThemeByDataAttribute({
themes: {
light: 'light',
dark: 'dark',
},
defaultTheme: 'light',
attributeName: 'data-theme',
}),
Story => {
return <I18nServer>
<Story />
</I18nServer>
}
];
withThemeByDataAttribute({
themes: {
light: 'light',
dark: 'dark',
},
defaultTheme: 'light',
attributeName: 'data-theme',
}),
(Story) => {
return <I18nServer>
<Story />
</I18nServer>
},
]
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
}
export default preview

View file

@ -21,5 +21,6 @@
"editor.defaultFormatter": "vscode.json-language-features"
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
"typescript.enablePromptUseWorkspaceTsdk": true,
"npm.packageManager": "pnpm"
}

View file

@ -6,6 +6,9 @@ LABEL maintainer="takatost@gmail.com"
# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache tzdata
RUN npm install -g pnpm@9.12.2
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# install packages
@ -14,12 +17,12 @@ FROM base AS packages
WORKDIR /app/web
COPY package.json .
COPY yarn.lock .
COPY pnpm-lock.yaml .
# if you located in China, you can use taobao registry to speed up
# RUN yarn install --frozen-lockfile --registry https://registry.npmmirror.com/
# RUN pnpm install --frozen-lockfile --registry https://registry.npmmirror.com/
RUN yarn install --frozen-lockfile
RUN pnpm install --frozen-lockfile
# build resources
FROM base AS builder
@ -27,7 +30,8 @@ WORKDIR /app/web
COPY --from=packages /app/web/ .
COPY . .
RUN yarn build
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN pnpm build
# production stage
@ -38,6 +42,8 @@ ENV EDITION=SELF_HOSTED
ENV DEPLOY_ENV=PRODUCTION
ENV CONSOLE_API_URL=http://127.0.0.1:5001
ENV APP_API_URL=http://127.0.0.1:5001
ENV MARKETPLACE_API_URL=http://127.0.0.1:5001
ENV MARKETPLACE_URL=http://127.0.0.1:5001
ENV PORT=3000
ENV NEXT_TELEMETRY_DISABLED=1
@ -57,8 +63,7 @@ COPY docker/entrypoint.sh ./entrypoint.sh
# global runtime packages
RUN yarn global add pm2 \
&& yarn cache clean \
RUN pnpm add -g pm2 \
&& mkdir /.pm2 \
&& chown -R 1001:0 /.pm2 /app/web \
&& chmod -R g=u /.pm2 /app/web

View file

@ -6,14 +6,12 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next
### Run by source code
To start the web frontend service, you will need [Node.js v18.x (LTS)](https://nodejs.org/en) and [NPM version 8.x.x](https://www.npmjs.com/) or [Yarn](https://yarnpkg.com/).
To start the web frontend service, you will need [Node.js v18.x (LTS)](https://nodejs.org/en) and [pnpm version 9.12.2](https://pnpm.io).
First, install the dependencies:
```bash
npm install
# or
yarn install --frozen-lockfile
pnpm install
```
Then, configure the environment variables. Create a file named `.env.local` in the current directory and copy the contents from `.env.example`. Modify the values of these environment variables according to your requirements:
@ -43,9 +41,7 @@ NEXT_PUBLIC_SENTRY_DSN=
Finally, run the development server:
```bash
npm run dev
# or
yarn dev
pnpm run dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
@ -59,19 +55,19 @@ You can start editing the file under folder `app`. The page auto-updates as you
First, build the app for production:
```bash
npm run build
pnpm run build
```
Then, start the server:
```bash
npm run start
pnpm run start
```
If you want to customize the host and port:
```bash
npm run start --port=3001 --host=0.0.0.0
pnpm run start --port=3001 --host=0.0.0.0
```
## Storybook
@ -81,7 +77,7 @@ This project uses [Storybook](https://storybook.js.org/) for UI component develo
To start the storybook server, run:
```bash
yarn storybook
pnpm storybook
```
Open [http://localhost:6006](http://localhost:6006) with your browser to see the result.
@ -99,7 +95,7 @@ You can create a test file with a suffix of `.spec` beside the file that to be t
Run test:
```bash
npm run test
pnpm run test
```
If you are not familiar with writing tests, here is some code to refer to:

View file

@ -1,5 +1,5 @@
import React from 'react'
import { type Locale } from '@/i18n'
import type { Locale } from '@/i18n'
import DevelopMain from '@/app/components/develop'
export type IDevelopProps = {

View file

@ -106,6 +106,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
// if ((appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (pathname).endsWith('workflow'))
// setAppSiderbarExpand('collapse')
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [appDetail, isMobile])
useEffect(() => {

View file

@ -46,7 +46,7 @@ export default function ChartView({ appId }: IChartViewProps) {
return (
<div>
<div className='flex flex-row items-center mt-8 mb-4 text-gray-900 text-base'>
<div className='flex flex-row items-center mt-8 mb-4 system-xl-semibold text-text-primary'>
<span className='mr-3'>{t('appOverview.analysis.title')}</span>
<SimpleSelect
items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))}

View file

@ -12,7 +12,7 @@ const Overview = async ({
params: { appId },
}: IDevelopProps) => {
return (
<div className="h-full px-4 sm:px-16 py-6 overflow-scroll">
<div className="h-full px-4 sm:px-12 py-6 overflow-scroll bg-chatbot-bg">
<ApikeyInfoPanel />
<TracingPanel />
<CardView appId={appId} />

View file

@ -1,20 +1,18 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiEqualizer2Line,
} from '@remixicon/react'
import type { PopupProps } from './config-popup'
import ConfigPopup from './config-popup'
import cn from '@/utils/classnames'
import Button from '@/app/components/base/button'
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
const I18N_PREFIX = 'app.tracing'
type Props = {
readOnly: boolean
className?: string
@ -28,7 +26,6 @@ const ConfigBtn: FC<Props> = ({
controlShowPopup,
...popupProps
}) => {
const { t } = useTranslation()
const [open, doSetOpen] = useState(false)
const openRef = useRef(open)
const setOpen = useCallback((v: boolean) => {
@ -50,21 +47,6 @@ const ConfigBtn: FC<Props> = ({
if (popupProps.readOnly && !hasConfigured)
return null
const triggerContent = hasConfigured
? (
<div className={cn(className, 'p-1 rounded-md hover:bg-black/5 cursor-pointer')}>
<Settings04 className='w-4 h-4 text-gray-500' />
</div>
)
: (
<Button variant='primary'
className={cn(className, '!h-8 !px-3 select-none')}
>
<Settings04 className='mr-1 w-4 h-4' />
<span className='text-[13px]'>{t(`${I18N_PREFIX}.config`)}</span>
</Button>
)
return (
<PortalToFollowElem
open={open}
@ -72,11 +54,13 @@ const ConfigBtn: FC<Props> = ({
placement='bottom-end'
offset={{
mainAxis: 12,
crossAxis: hasConfigured ? 8 : 0,
crossAxis: hasConfigured ? 8 : 49,
}}
>
<PortalToFollowElemTrigger onClick={handleTrigger}>
{triggerContent}
<div className={cn(className, 'p-1 rounded-md')}>
<RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[11]'>
<ConfigPopup {...popupProps} />

View file

@ -11,6 +11,8 @@ import ProviderConfigModal from './provider-config-modal'
import Indicator from '@/app/components/header/indicator'
import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import Divider from '@/app/components/base/divider'
import cn from '@/utils/classnames'
const I18N_PREFIX = 'app.tracing'
@ -79,7 +81,6 @@ const ConfigPopup: FC<PopupProps> = ({
className='ml-3'
defaultValue={enabled}
onChange={onStatusChange}
size='l'
disabled={providerAllNotConfigured}
/>
)
@ -123,7 +124,7 @@ const ConfigPopup: FC<PopupProps> = ({
)
const configuredProviderPanel = () => {
const configuredPanels: ProviderPanel[] = []
const configuredPanels: any[] = []
if (langSmithConfig)
configuredPanels.push(langSmithPanel)
@ -138,7 +139,7 @@ const ConfigPopup: FC<PopupProps> = ({
}
const moreProviderPanel = () => {
const notConfiguredPanels: ProviderPanel[] = []
const notConfiguredPanels: any[] = []
if (!langSmithConfig)
notConfiguredPanels.push(langSmithPanel)
@ -161,15 +162,15 @@ const ConfigPopup: FC<PopupProps> = ({
}
return (
<div className='w-[420px] p-4 rounded-2xl bg-white border-[0.5px] border-black/5 shadow-lg'>
<div className='w-[420px] p-4 rounded-2xl bg-components-panel-bg border-[0.5px] border-components-panel-border shadow-xl'>
<div className='flex justify-between items-center'>
<div className='flex items-center'>
<TracingIcon size='md' className='mr-2' />
<div className='leading-[120%] text-[18px] font-semibold text-gray-900'>{t(`${I18N_PREFIX}.tracing`)}</div>
<div className='text-text-primary title-2xl-semibold'>{t(`${I18N_PREFIX}.tracing`)}</div>
</div>
<div className='flex items-center'>
<Indicator color={enabled ? 'green' : 'gray'} />
<div className='ml-1.5 text-xs font-semibold text-gray-500 uppercase'>
<div className={cn('ml-1 system-xs-semibold-uppercase text-text-tertiary', enabled && 'text-util-colors-green-green-600')}>
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
</div>
{!readOnly && (
@ -185,19 +186,18 @@ const ConfigPopup: FC<PopupProps> = ({
: switchContent}
</>
)}
</div>
</div>
<div className='mt-2 leading-4 text-xs font-normal text-gray-500'>
<div className='mt-2 system-xs-regular text-text-tertiary'>
{t(`${I18N_PREFIX}.tracingDescription`)}
</div>
<div className='mt-3 h-px bg-gray-100'></div>
<div className='mt-3'>
<Divider className='my-3' />
<div className='relative'>
{(providerAllConfigured || providerAllNotConfigured)
? (
<>
<div className='leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}</div>
<div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}</div>
<div className='mt-2 space-y-2'>
{langSmithPanel}
{langfusePanel}
@ -207,11 +207,11 @@ const ConfigPopup: FC<PopupProps> = ({
)
: (
<>
<div className='leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${I18N_PREFIX}.configProviderTitle.configured`)}</div>
<div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${I18N_PREFIX}.configProviderTitle.configured`)}</div>
<div className='mt-2 space-y-2'>
{configuredProviderPanel()}
</div>
<div className='mt-3 leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}</div>
<div className='mt-3 system-xs-medium-uppercase text-text-tertiary'>{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}</div>
<div className='mt-2 space-y-2'>
{moreProviderPanel()}
</div>

View file

@ -26,7 +26,7 @@ const Field: FC<Props> = ({
return (
<div className={cn(className)}>
<div className='flex py-[7px]'>
<div className={cn(labelClassName, 'flex items-center h-[18px] text-[13px] font-medium text-gray-900')}>{label} </div>
<div className={cn(labelClassName, 'flex items-center h-[18px] text-[13px] font-medium text-text-primary')}>{label} </div>
{isRequired && <span className='ml-0.5 text-xs font-semibold text-[#D92D20]'>*</span>}
</div>
<Input

View file

@ -1,10 +1,13 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import {
RiArrowDownDoubleLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { usePathname } from 'next/navigation'
import { useBoolean } from 'ahooks'
import type { LangFuseConfig, LangSmithConfig } from './type'
import type { LangFuseConfig, LangSmithConfig, OpikConfig } from './type'
import { TracingProvider } from './type'
import TracingIcon from './tracing-icon'
import ConfigButton from './config-button'
@ -16,6 +19,7 @@ import type { TracingStatus } from '@/models/app'
import Toast from '@/app/components/base/toast'
import { useAppContext } from '@/context/app-context'
import Loading from '@/app/components/base/loading'
import Divider from '@/app/components/base/divider'
const I18N_PREFIX = 'app.tracing'
@ -27,7 +31,7 @@ const Title = ({
const { t } = useTranslation()
return (
<div className={cn(className, 'flex items-center text-lg font-semibold text-gray-900')}>
<div className={cn('flex items-center system-xl-semibold text-text-primary', className)}>
{t('common.appMenus.overview')}
</div>
)
@ -102,7 +106,7 @@ const Panel: FC = () => {
const { tracing_config } = await doFetchTracingConfig({ appId, provider })
if (provider === TracingProvider.langSmith)
setLangSmithConfig(tracing_config as LangSmithConfig)
else if (provider === TracingProvider.langSmith)
else if (provider === TracingProvider.langfuse)
setLangFuseConfig(tracing_config as LangFuseConfig)
else if (provider === TracingProvider.opik)
setOpikConfig(tracing_config as OpikConfig)
@ -111,7 +115,7 @@ const Panel: FC = () => {
const handleTracingConfigRemoved = (provider: TracingProvider) => {
if (provider === TracingProvider.langSmith)
setLangSmithConfig(null)
else if (provider === TracingProvider.langSmith)
else if (provider === TracingProvider.langfuse)
setLangFuseConfig(null)
else if (provider === TracingProvider.opik)
setOpikConfig(null)
@ -151,46 +155,72 @@ const Panel: FC = () => {
return (
<div className={cn('mb-3 flex justify-between items-center')}>
<Title className='h-[41px]' />
<div className='flex items-center p-2 rounded-xl border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100' onClick={showPopup}>
{!inUseTracingProvider
? <>
<TracingIcon size='md' className='mr-2' />
<div className='leading-5 text-sm font-semibold text-gray-700'>{t(`${I18N_PREFIX}.title`)}</div>
</>
: <InUseProviderIcon className='ml-1 h-4' />}
{hasConfiguredTracing && (
<div className='ml-4 mr-1 flex items-center'>
<Indicator color={enabled ? 'green' : 'gray'} />
<div className='ml-1.5 text-xs font-semibold text-gray-500 uppercase'>
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
<div
className={cn(
'flex items-center p-2 rounded-xl bg-background-default-dodge border-t border-l-[0.5px] border-effects-highlight shadow-xs cursor-pointer hover:bg-background-default-lighter hover:border-effects-highlight-lightmode-off',
controlShowPopup && 'bg-background-default-lighter border-effects-highlight-lightmode-off',
)}
onClick={showPopup}
>
{!inUseTracingProvider && (
<>
<TracingIcon size='md' />
<div className='mx-2 system-sm-semibold text-text-secondary'>{t(`${I18N_PREFIX}.title`)}</div>
<div className='flex items-center' onClick={e => e.stopPropagation()}>
<ConfigButton
appId={appId}
readOnly={readOnly}
hasConfigured={false}
enabled={enabled}
onStatusChange={handleTracingEnabledChange}
chosenProvider={inUseTracingProvider}
onChooseProvider={handleChooseProvider}
langSmithConfig={langSmithConfig}
langFuseConfig={langFuseConfig}
opikConfig={opikConfig}
onConfigUpdated={handleTracingConfigUpdated}
onConfigRemoved={handleTracingConfigRemoved}
controlShowPopup={controlShowPopup}
/>
</div>
</div>
<Divider type='vertical' className='h-3.5' />
<div className='p-1 rounded-md'>
<RiArrowDownDoubleLine className='w-4 h-4 text-text-tertiary' />
</div>
</>
)}
{hasConfiguredTracing && (
<div className='ml-2 w-px h-3.5 bg-gray-200'></div>
<>
<div className='ml-4 mr-1 flex items-center'>
<Indicator color={enabled ? 'green' : 'gray'} />
<div className='ml-1.5 system-xs-semibold-uppercase text-text-tertiary'>
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
</div>
</div>
<InUseProviderIcon className='ml-1 h-4' />
<Divider type='vertical' className='h-3.5' />
<div className='flex items-center' onClick={e => e.stopPropagation()}>
<ConfigButton
appId={appId}
readOnly={readOnly}
hasConfigured
className='ml-2'
enabled={enabled}
onStatusChange={handleTracingEnabledChange}
chosenProvider={inUseTracingProvider}
onChooseProvider={handleChooseProvider}
langSmithConfig={langSmithConfig}
langFuseConfig={langFuseConfig}
opikConfig={opikConfig}
onConfigUpdated={handleTracingConfigUpdated}
onConfigRemoved={handleTracingConfigRemoved}
controlShowPopup={controlShowPopup}
/>
</div>
</>
)}
<div className='flex items-center' onClick={e => e.stopPropagation()}>
<ConfigButton
appId={appId}
readOnly={readOnly}
hasConfigured
className='ml-2'
enabled={enabled}
onStatusChange={handleTracingEnabledChange}
chosenProvider={inUseTracingProvider}
onChooseProvider={handleChooseProvider}
langSmithConfig={langSmithConfig}
langFuseConfig={langFuseConfig}
opikConfig={opikConfig}
onConfigUpdated={handleTracingConfigUpdated}
onConfigRemoved={handleTracingConfigRemoved}
controlShowPopup={controlShowPopup}
/>
</div>
</div>
</div>
</div >
</div >
)
}
export default React.memo(Panel)

View file

@ -17,6 +17,7 @@ import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/gene
import Confirm from '@/app/components/base/confirm'
import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps'
import Toast from '@/app/components/base/toast'
import Divider from '@/app/components/base/divider'
type Props = {
appId: string
@ -122,7 +123,8 @@ const ProviderConfigModal: FC<Props> = ({
}
if (type === TracingProvider.opik) {
const postData = config as OpikConfig
// todo: check field validity
// const postData = config as OpikConfig
}
return errorMessage
@ -166,11 +168,11 @@ const ProviderConfigModal: FC<Props> = ({
? (
<PortalToFollowElem open>
<PortalToFollowElemContent className='w-full h-full z-[60]'>
<div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
<div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
<div className='fixed inset-0 flex items-center justify-center bg-background-overlay'>
<div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-components-panel-bg shadow-xl rounded-2xl overflow-y-auto'>
<div className='px-8 pt-8'>
<div className='flex justify-between items-center mb-4'>
<div className='text-xl font-semibold text-gray-900'>{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}</div>
<div className='title-2xl-semibold text-text-primary'>{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}</div>
</div>
<div className='space-y-4'>
@ -276,16 +278,16 @@ const ProviderConfigModal: FC<Props> = ({
{isEdit && (
<>
<Button
className='h-9 text-sm font-medium text-gray-700'
className='h-9 text-sm font-medium text-text-secondary'
onClick={showRemoveConfirm}
>
<span className='text-[#D92D20]'>{t('common.operation.remove')}</span>
</Button>
<div className='mx-3 w-px h-[18px] bg-gray-200'></div>
<Divider className='mx-3 h-[18px]' />
</>
)}
<Button
className='mr-2 h-9 text-sm font-medium text-gray-700'
className='mr-2 h-9 text-sm font-medium text-text-secondary'
onClick={onCancel}
>
{t('common.operation.cancel')}
@ -302,9 +304,9 @@ const ProviderConfigModal: FC<Props> = ({
</div>
</div>
<div className='border-t-[0.5px] border-t-black/5'>
<div className='flex justify-center items-center py-3 bg-gray-50 text-xs text-gray-500'>
<Lock01 className='mr-1 w-3 h-3 text-gray-500' />
<div className='border-t-[0.5px] border-divider-regular'>
<div className='flex justify-center items-center py-3 bg-background-section-burn text-xs text-text-tertiary'>
<Lock01 className='mr-1 w-3 h-3 text-text-tertiary' />
{t('common.modelProvider.encrypted.front')}
<a
className='text-primary-600 mx-1'

View file

@ -1,11 +1,13 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import {
RiEqualizer2Line,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { TracingProvider } from './type'
import cn from '@/utils/classnames'
import { LangfuseIconBig, LangsmithIconBig, OpikIconBig } from '@/app/components/base/icons/src/public/tracing'
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'
@ -62,34 +64,37 @@ const ProviderPanel: FC<Props> = ({
}, [hasConfigured, isChosen, onChoose, readOnly])
return (
<div
className={cn(isChosen ? 'border-primary-400' : 'border-transparent', !isChosen && hasConfigured && !readOnly && 'cursor-pointer', 'px-4 py-3 rounded-xl border-[1.5px] bg-gray-100')}
className={cn(
'px-4 py-3 rounded-xl border-[1.5px] bg-background-section-burn',
isChosen ? 'bg-background-section border-components-option-card-option-selected-border' : 'border-transparent',
!isChosen && hasConfigured && !readOnly && 'cursor-pointer',
)}
onClick={handleChosen}
>
<div className={'flex justify-between items-center space-x-1'}>
<div className='flex items-center'>
<Icon className='h-6' />
{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-text-accent-secondary system-2xs-medium-uppercase text-text-accent-secondary'>{t(`${I18N_PREFIX}.inUse`)}</div>}
</div>
{!readOnly && (
<div className={'flex justify-between items-center space-x-1'}>
{hasConfigured && (
<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'/>
<div className='flex px-2 items-center h-6 bg-components-button-secondary-bg rounded-md border-[0.5px] border-components-button-secondary-border shadow-xs cursor-pointer text-text-secondary space-x-1' onClick={viewBtnClick} >
<View className='w-3 h-3' />
<div className='text-xs font-medium'>{t(`${I18N_PREFIX}.view`)}</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'
className='flex px-2 items-center h-6 bg-components-button-secondary-bg rounded-md border-[0.5px] border-components-button-secondary-border shadow-xs cursor-pointer text-text-secondary space-x-1'
onClick={handleConfigBtnClick}
>
<Settings04 className='w-3 h-3' />
<RiEqualizer2Line className='w-3 h-3' />
<div className='text-xs font-medium'>{t(`${I18N_PREFIX}.config`)}</div>
</div>
</div>
)}
</div>
<div className='mt-2 leading-4 text-xs font-normal text-gray-500'>
<div className='mt-2 system-xs-regular text-text-tertiary'>
{t(`${I18N_PREFIX}.${type}.description`)}
</div>
</div>

View file

@ -1,45 +0,0 @@
'use client'
import { ChevronDoubleDownIcon } from '@heroicons/react/20/solid'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import React, { useCallback } from 'react'
import Tooltip from '@/app/components/base/tooltip'
const I18N_PREFIX = 'app.tracing'
type Props = {
isFold: boolean
onFoldChange: (isFold: boolean) => void
}
const ToggleFoldBtn: FC<Props> = ({
isFold,
onFoldChange,
}) => {
const { t } = useTranslation()
const handleFoldChange = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
onFoldChange(!isFold)
}, [isFold, onFoldChange])
return (
// text-[0px] to hide spacing between tooltip elements
<div className='shrink-0 cursor-pointer text-[0px]' onClick={handleFoldChange}>
<Tooltip
popupContent={t(`${I18N_PREFIX}.${isFold ? 'expand' : 'collapse'}`)}
>
{isFold && (
<div className='p-1 rounded-md text-gray-500 hover:text-gray-800 hover:bg-black/5'>
<ChevronDoubleDownIcon className='w-4 h-4' />
</div>
)}
{!isFold && (
<div className='p-2 rounded-lg text-gray-500 border-[0.5px] border-gray-200 hover:text-gray-800 hover:bg-black/5'>
<ChevronDoubleDownIcon className='w-4 h-4 transform rotate-180' />
</div>
)}
</Tooltip>
</div>
)
}
export default React.memo(ToggleFoldBtn)

View file

@ -15,6 +15,7 @@ const AppDetail: FC<IAppDetail> = ({ children }) => {
useEffect(() => {
if (isCurrentWorkspaceDatasetOperator)
return router.replace('/datasets')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isCurrentWorkspaceDatasetOperator])
return (

View file

@ -71,6 +71,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
})
}
setShowConfirmDelete(false)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [app.id])
const onEdit: CreateAppModalProps['onConfirm'] = useCallback(async ({
@ -100,7 +101,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
onRefresh()
mutateApps()
}
catch (e) {
catch {
notify({ type: 'error', message: t('app.editFailed') })
}
}, [app.id, mutateApps, notify, onRefresh, t])
@ -127,7 +128,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
onPlanInfoChanged()
getRedirection(isCurrentWorkspaceEditor, newApp, push)
}
catch (e) {
catch {
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
}
@ -144,7 +145,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
a.download = `${app.name}.yml`
a.click()
}
catch (e) {
catch {
notify({ type: 'error', message: t('app.exportFailed') })
}
}
@ -163,7 +164,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
}
setSecretEnvList(list)
}
catch (e) {
catch {
notify({ type: 'error', message: t('app.exportFailed') })
}
}

View file

@ -30,7 +30,7 @@ const ApiServer: FC<ApiServerProps> = ({
{t('appApi.ok')}
</div>
<SecretKeyButton
className='flex-shrink-0 !h-8 bg-white'
className='shrink-0 !h-8 bg-white'
/>
</div>
)

View file

@ -78,7 +78,7 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
onClick={() => setIsTocExpanded(false)}
className="text-gray-500 hover:text-gray-700"
>
✕
✕
</button>
</div>
<ul className="space-y-2">

View file

@ -0,0 +1,20 @@
import PluginPage from '@/app/components/plugins/plugin-page'
import PluginsPanel from '@/app/components/plugins/plugin-page/plugins-panel'
import Marketplace from '@/app/components/plugins/marketplace'
import { getLocaleOnServer } from '@/i18n/server'
const PluginList = async () => {
const locale = await getLocaleOnServer()
return (
<PluginPage
plugins={<PluginsPanel />}
marketplace={<Marketplace locale={locale} pluginTypeSwitchClassName='top-[60px]' searchBoxAutoAnimate={false} />}
/>
)
}
export const metadata = {
title: 'Plugins - Dify',
}
export default PluginList

View file

@ -8,7 +8,7 @@ import { logout } from '@/service/common'
import { useAppContext } from '@/context/app-context'
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
export type IAppSelector = {
export interface IAppSelector {
isMobile: boolean
}

View file

@ -60,12 +60,12 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
return (
<div className="flex items-center grow">
{icon && icon_background && iconType === 'app' && (
<div className='flex-shrink-0 mr-3'>
<div className='shrink-0 mr-3'>
<AppIcon icon={icon} background={icon_background} />
</div>
)}
{iconType !== 'app'
&& <div className='flex-shrink-0 mr-3'>
&& <div className='shrink-0 mr-3'>
{ICON_MAP[iconType]}
</div>

View file

@ -6,11 +6,11 @@ import useSWR from 'swr'
import Input from '@/app/components/base/input'
import { fetchAnnotationsCount } from '@/service/log'
export type QueryParam = {
export interface QueryParam {
keyword?: string
}
type IFilterProps = {
interface IFilterProps {
appId: string
queryParams: QueryParam
setQueryParams: (v: QueryParam) => void

View file

@ -9,7 +9,7 @@ import ActionButton from '@/app/components/base/action-button'
import useTimestamp from '@/hooks/use-timestamp'
import cn from '@/utils/classnames'
type Props = {
interface Props {
list: AnnotationItem[]
onRemove: (id: string) => void
onView: (item: AnnotationItem) => void

View file

@ -143,22 +143,19 @@ const AppPublisher = ({
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[11]'>
<div className='w-[336px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl'>
<div className='w-[336px] bg-components-panel-bg rounded-2xl border-[0.5px] border-components-panel-border shadow-xl'>
<div className='p-4 pt-3'>
<div className='flex items-center h-6 text-xs font-medium text-gray-500 uppercase'>
<div className='flex items-center h-6 system-xs-medium-uppercase text-text-tertiary'>
{publishedAt ? t('workflow.common.latestPublished') : t('workflow.common.currentDraftUnpublished')}
</div>
{publishedAt
? (
<div className='flex justify-between items-center h-[18px]'>
<div className='flex items-center mt-[3px] mb-[3px] leading-[18px] text-[13px] font-medium text-gray-700'>
<div className='flex items-center mt-[3px] mb-[3px] leading-[18px] text-[13px] font-medium text-text-secondary'>
{t('workflow.common.publishedAt')} {formatTimeFromNow(publishedAt)}
</div>
<Button
className={`
ml-2 px-2 text-primary-600
${published && 'text-primary-300 border-gray-100'}
`}
variant='secondary-accent'
size='small'
onClick={handleRestore}
disabled={published}
@ -168,7 +165,7 @@ const AppPublisher = ({
</div>
)
: (
<div className='flex items-center h-[18px] leading-[18px] text-[13px] font-medium text-gray-700'>
<div className='flex items-center h-[18px] leading-[18px] text-[13px] font-medium text-text-secondary'>
{t('workflow.common.autoSaved')} · {Boolean(draftUpdatedAt) && formatTimeFromNow(draftUpdatedAt!)}
</div>
)}
@ -196,7 +193,7 @@ const AppPublisher = ({
)
}
</div>
<div className='p-4 pt-3 border-t-[0.5px] border-t-black/5'>
<div className='p-4 pt-3 border-t-[0.5px] border-divider-regular'>
<SuggestedAction disabled={!publishedAt} link={appURL} icon={<PlayCircle />}>{t('workflow.common.runApp')}</SuggestedAction>
{appDetail?.mode === 'workflow'
? (

View file

@ -77,21 +77,21 @@ const PublishWithMultipleModel: FC<PublishWithMultipleModelProps> = ({
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='mt-1 w-[288px] z-50'>
<div className='p-1 rounded-lg border-[0.5px] border-gray-200 shadow-lg bg-white'>
<div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>
<div className='p-1 rounded-lg border-[0.5px] border-components-panel-border shadow-lg bg-components-panel-bg'>
<div className='flex items-center px-3 h-[22px] text-xs font-medium text-text-tertiary'>
{t('appDebug.publishAs')}
</div>
{
validModelConfigs.map((item, index) => (
<div
key={item.id}
className='flex items-center h-8 px-3 text-sm text-gray-500 rounded-lg cursor-pointer hover:bg-gray-100'
className='flex items-center h-8 px-3 text-sm text-text-tertiary rounded-lg cursor-pointer hover:bg-state-base-hover'
onClick={() => handleSelect(item)}
>
<span className='italic min-w-[18px]'>#{index + 1}</span>
<ModelIcon modelName={item.model} provider={item.providerItem} className='ml-2' />
<div
className='ml-1 text-gray-700 truncate'
className='ml-1 text-text-secondary truncate'
title={item.modelItem.label[language]}
>
{item.modelItem.label[language]}

View file

@ -14,8 +14,8 @@ const SuggestedAction = ({ icon, link, disabled, children, className, ...props }
target='_blank'
rel='noreferrer'
className={classNames(
'flex justify-start items-center gap-2 h-[34px] px-2.5 bg-gray-100 rounded-lg transition-colors [&:not(:first-child)]:mt-1',
disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'hover:bg-primary-50 hover:text-primary-600 cursor-pointer',
'flex justify-start items-center gap-2 h-[34px] px-2.5 bg-background-section-burn rounded-lg transition-colors text-text-secondary [&:not(:first-child)]:mt-1',
disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'hover:bg-state-accent-hover hover:text-text-accent cursor-pointer',
className,
)}
{...props}

View file

@ -23,7 +23,7 @@ const FeaturePanel: FC<IFeaturePanelProps> = ({
children,
}) => {
return (
<div className={cn('rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn pb-3', noBodySpacing && '!pb-0', className)}>
<div className={cn('rounded-xl border-t-[0.5px] border-l-[0.5px] border-effects-highlight bg-background-section-burn pb-3', noBodySpacing && 'pb-0', className)}>
{/* Header */}
<div className={cn('px-3 pt-2', hasHeaderBottomBorder && 'border-b border-divider-subtle')}>
<div className='flex justify-between items-center h-8'>

View file

@ -2,7 +2,10 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { PlusIcon } from '@heroicons/react/20/solid'
import {
RiAddLine,
RiEditLine,
} from '@remixicon/react'
import cn from '@/utils/classnames'
export type IOperationBtnProps = {
@ -13,11 +16,8 @@ export type IOperationBtnProps = {
}
const iconMap = {
add: <PlusIcon className='w-3.5 h-3.5' />,
edit: (<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.99998 11.6666H12.25M1.75 11.6666H2.72682C3.01217 11.6666 3.15485 11.6666 3.28912 11.6344C3.40816 11.6058 3.52196 11.5587 3.62635 11.4947C3.74408 11.4226 3.84497 11.3217 4.04675 11.1199L11.375 3.79164C11.8583 3.30839 11.8583 2.52488 11.375 2.04164C10.8918 1.55839 10.1083 1.55839 9.62501 2.04164L2.29674 9.3699C2.09496 9.57168 1.99407 9.67257 1.92192 9.7903C1.85795 9.89469 1.81081 10.0085 1.78224 10.1275C1.75 10.2618 1.75 10.4045 1.75 10.6898V11.6666Z" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
add: <RiAddLine className='w-3.5 h-3.5' />,
edit: <RiEditLine className='w-3.5 h-3.5' />,
}
const OperationBtn: FC<IOperationBtnProps> = ({
@ -29,7 +29,7 @@ const OperationBtn: FC<IOperationBtnProps> = ({
const { t } = useTranslation()
return (
<div
className={cn(className, 'flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200 select-none')}
className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-text-secondary cursor-pointer hover:bg-state-base-hover select-none', className)}
onClick={onClick}>
<div>
{iconMap[type]}

View file

@ -28,8 +28,8 @@ const EditModal: FC<Props> = ({
isShow={isShow}
onClose={onClose}
>
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('appDebug.feature.conversationHistory.editModal.userPrefix')}</div>
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
<div className={'mt-6 font-medium text-sm leading-[21px] text-text-primary'}>{t('appDebug.feature.conversationHistory.editModal.userPrefix')}</div>
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-components-input-bg-normal'}
value={tempData.user_prefix}
onChange={e => setTempData({
...tempData,
@ -37,8 +37,8 @@ const EditModal: FC<Props> = ({
})}
/>
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('appDebug.feature.conversationHistory.editModal.assistantPrefix')}</div>
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
<div className={'mt-6 font-medium text-sm leading-[21px] text-text-primary'}>{t('appDebug.feature.conversationHistory.editModal.assistantPrefix')}</div>
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-components-input-bg-normal'}
value={tempData.assistant_prefix}
onChange={e => setTempData({
...tempData,
@ -48,8 +48,8 @@ const EditModal: FC<Props> = ({
/>
<div className='mt-10 flex justify-end'>
<Button className='mr-2 flex-shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button variant='primary' className='flex-shrink-0' onClick={() => onSave(tempData)} loading={saveLoading}>{t('common.operation.save')}</Button>
<Button className='mr-2 shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button variant='primary' className='shrink-0' onClick={() => onSave(tempData)} loading={saveLoading}>{t('common.operation.save')}</Button>
</div>
</Modal>
)

View file

@ -30,20 +30,20 @@ const HistoryPanel: FC<Props> = ({
</div>
}
headerIcon={
<div className='p-1 rounded-md bg-white shadow-xs'>
<div className='p-1 rounded-md shadow-xs'>
<MessageClockCircle className='w-4 h-4 text-[#DD2590]' />
</div>}
headerRight={
<div className='flex items-center'>
<div className='text-xs text-gray-500'>{t('appDebug.feature.conversationHistory.description')}</div>
<div className='ml-3 w-[1px] h-[14px] bg-gray-200'></div>
<div className='text-xs text-text-tertiary'>{t('appDebug.feature.conversationHistory.description')}</div>
<div className='ml-3 w-[1px] h-[14px] bg-divider-regular'></div>
<OperationBtn type="edit" onClick={onShowEditModal} />
</div>
}
noBodySpacing
>
{showWarning && (
<div className='flex justify-between py-2 px-3 rounded-b-xl bg-[#FFFAEB] text-xs text-gray-700'>
<div className='flex justify-between py-2 px-3 rounded-b-xl bg-background-section-burn text-xs text-text-secondary'>
<div>{t('appDebug.feature.conversationHistory.tip')}
<a href={`${locale === LanguagesSupported[1]
? 'https://docs.dify.ai/v/zh-hans/guides/application-design/prompt-engineering'

View file

@ -9,7 +9,7 @@ import ConfirmAddVar from './confirm-add-var'
import s from './style.module.css'
import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap'
import cn from '@/utils/classnames'
import { type PromptVariable } from '@/models/debug'
import type { PromptVariable } from '@/models/debug'
import Tooltip from '@/app/components/base/tooltip'
import type { CompletionParams } from '@/types/app'
import { AppType } from '@/types/app'

View file

@ -3,7 +3,7 @@ import type { FC } from 'react'
import React from 'react'
import cn from '@/utils/classnames'
type Props = {
interface Props {
className?: string
title: string
children: JSX.Element

View file

@ -3,7 +3,7 @@ import type { FC } from 'react'
import React, { useEffect } from 'react'
import Input from '@/app/components/base/input'
export type IConfigStringProps = {
export interface IConfigStringProps {
value: number | undefined
maxLength: number
modelId: string
@ -28,7 +28,7 @@ const ConfigString: FC<IConfigStringProps> = ({
min={1}
value={value || ''}
onChange={(e) => {
let value = parseInt(e.target.value, 10)
let value = Number.parseInt(e.target.value, 10)
if (value > maxLength)
value = maxLength

View file

@ -3,26 +3,17 @@ import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import type { Timeout } from 'ahooks/lib/useRequest/src/types'
import { useContext } from 'use-context-selector'
import produce from 'immer'
import {
RiDeleteBinLine,
} from '@remixicon/react'
import Panel from '../base/feature-panel'
import EditModal from './config-modal'
import IconTypeIcon from './input-type-icon'
import type { IInputTypeIconProps } from './input-type-icon'
import s from './style.module.css'
import VarItem from './var-item'
import SelectVarType from './select-var-type'
import { BracketsX as VarIcon } from '@/app/components/base/icons/src/vender/line/development'
import Tooltip from '@/app/components/base/tooltip'
import type { PromptVariable } from '@/models/debug'
import { DEFAULT_VALUE_MAX_LEN, getMaxVarNameLength } from '@/config'
import { checkKeys, getNewVar } from '@/utils/var'
import Switch from '@/app/components/base/switch'
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
import { getNewVar } from '@/utils/var'
import Toast from '@/app/components/base/toast'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import Confirm from '@/app/components/base/confirm'
import ConfigContext from '@/context/debug-configuration'
import { AppType } from '@/types/app'
@ -50,8 +41,6 @@ export type IConfigVarProps = {
onPromptVariablesChange?: (promptVariables: PromptVariable[]) => void
}
let conflictTimer: Timeout
const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVariablesChange }) => {
const { t } = useTranslation()
const {
@ -61,19 +50,6 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
const { eventEmitter } = useEventEmitterContextContext()
const hasVar = promptVariables.length > 0
const updatePromptVariable = (key: string, updateKey: string, newValue: string | boolean) => {
const newPromptVariables = promptVariables.map((item) => {
if (item.key === key) {
return {
...item,
[updateKey]: newValue,
}
}
return item
})
onPromptVariablesChange?.(newPromptVariables)
}
const [currIndex, setCurrIndex] = useState<number>(-1)
const currItem = currIndex !== -1 ? promptVariables[currIndex] : null
const currItemToEdit: InputVar | null = (() => {
@ -106,55 +82,6 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
onPromptVariablesChange?.(newPromptVariables)
}
const updatePromptKey = (index: number, newKey: string) => {
clearTimeout(conflictTimer)
const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true)
if (!isValid) {
Toast.notify({
type: 'error',
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
})
return
}
const newPromptVariables = promptVariables.map((item, i) => {
if (i === index) {
return {
...item,
key: newKey,
}
}
return item
})
conflictTimer = setTimeout(() => {
const isKeyExists = promptVariables.some(item => item.key?.trim() === newKey.trim())
if (isKeyExists) {
Toast.notify({
type: 'error',
message: t('appDebug.varKeyError.keyAlreadyExists', { key: newKey }),
})
}
}, 1000)
onPromptVariablesChange?.(newPromptVariables)
}
const updatePromptNameIfNameEmpty = (index: number, newKey: string) => {
if (!newKey)
return
const newPromptVariables = promptVariables.map((item, i) => {
if (i === index && !item.name) {
return {
...item,
name: newKey,
}
}
return item
})
onPromptVariablesChange?.(newPromptVariables)
}
const { setShowExternalDataToolModal } = useModalContext()
@ -273,9 +200,6 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
return (
<Panel
className="mt-2"
headerIcon={
<VarIcon className='w-4 h-4 text-primary-500' />
}
title={
<div className='flex items-center'>
<div className='mr-1'>{t('appDebug.variableTitle')}</div>
@ -291,87 +215,27 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
</div>
}
headerRight={!readonly ? <SelectVarType onChange={handleAddVar} /> : null}
noBodySpacing
>
{!hasVar && (
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div>
<div className='mt-1 px-3 pb-3'>
<div className='pt-2 pb-1 text-xs text-text-tertiary'>{t('appDebug.notSetVar')}</div>
</div>
)}
{hasVar && (
<div className='rounded-lg border border-gray-200 bg-white overflow-x-auto'>
<table className={`${s.table} min-w-[440px] w-full max-w-full border-collapse border-0 rounded-lg text-sm`}>
<thead className="border-b border-gray-200 text-gray-500 text-xs font-medium">
<tr className='uppercase'>
<td>{t('appDebug.variableTable.key')}</td>
<td>{t('appDebug.variableTable.name')}</td>
{!readonly && (
<>
<td>{t('appDebug.variableTable.optional')}</td>
<td>{t('appDebug.variableTable.action')}</td>
</>
)}
</tr>
</thead>
<tbody className="text-gray-700">
{promptVariables.map(({ key, name, type, required, config, icon, icon_background }, index) => (
<tr key={index} className="h-9 leading-9">
<td className="w-[160px] border-b border-gray-100 pl-3">
<div className='flex items-center space-x-1'>
<IconTypeIcon type={type as IInputTypeIconProps['type']} className='text-gray-400' />
{!readonly
? (
<input
type="text"
placeholder="key"
value={key}
onChange={e => updatePromptKey(index, e.target.value)}
onBlur={e => updatePromptNameIfNameEmpty(index, e.target.value)}
maxLength={getMaxVarNameLength(name)}
className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
/>
)
: (
<div className='h-6 leading-6 text-[13px] text-gray-700'>{key}</div>
)}
</div>
</td>
<td className="py-1 border-b border-gray-100">
{!readonly
? (
<input
type="text"
placeholder={key}
value={name}
onChange={e => updatePromptVariable(key, 'name', e.target.value)}
maxLength={getMaxVarNameLength(name)}
className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
/>)
: (
<div className='h-6 leading-6 text-[13px] text-gray-700'>{name}</div>
)}
</td>
{!readonly && (
<>
<td className='w-[84px] border-b border-gray-100'>
<div className='flex items-center h-full'>
<Switch defaultValue={!required} size='md' onChange={value => updatePromptVariable(key, 'required', !value)} />
</div>
</td>
<td className='w-20 border-b border-gray-100'>
<div className='flex h-full items-center space-x-1'>
<div className=' p-1 rounded-md hover:bg-black/5 w-6 h-6 cursor-pointer' onClick={() => handleConfig({ type, key, index, name, config, icon, icon_background })}>
<Settings01 className='w-4 h-4 text-gray-500' />
</div>
<div className=' p-1 rounded-md hover:bg-black/5 w-6 h-6 cursor-pointer' onClick={() => handleRemoveVar(index)} >
<RiDeleteBinLine className='w-4 h-4 text-gray-500' />
</div>
</div>
</td>
</>
)}
</tr>
))}
</tbody>
</table>
<div className='flex flex-wrap mt-1 px-3 pb-3 justify-between'>
{promptVariables.map(({ key, name, type, required, config, icon, icon_background }, index) => (
<VarItem
key={index}
readonly={readonly}
name={key}
label={name}
required={!!required}
type={type}
onEdit={() => handleConfig({ type, key, index, name, config, icon, icon_background })}
onRemove={() => handleRemoveVar(index)}
/>
))}
</div>
)}

View file

@ -27,7 +27,7 @@ const SelectTypeItem: FC<ISelectTypeItemProps> = ({
return (
<div
className={cn(
'flex flex-col justify-center items-center h-[58px] rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg space-y-1',
'flex flex-col justify-center items-center h-[58px] rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg space-y-1 text-text-secondary',
selected ? 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs system-xs-medium' : ' hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs cursor-pointer system-xs-regular')}
onClick={onClick}
>

View file

@ -1,12 +0,0 @@
.table td {
padding-left: 12px;
}
.table thead td {
height: 33px;
line-height: 33px;
}
.table tbody tr:last-child td {
border-bottom: none;
}

View file

@ -0,0 +1,74 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import {
RiDeleteBinLine,
RiEditLine,
} from '@remixicon/react'
import type { IInputTypeIconProps } from './input-type-icon'
import IconTypeIcon from './input-type-icon'
import { BracketsX as VarIcon } from '@/app/components/base/icons/src/vender/line/development'
import Badge from '@/app/components/base/badge'
import cn from '@/utils/classnames'
type ItemProps = {
readonly?: boolean
name: string
label: string
required: boolean
type: string
onEdit: () => void
onRemove: () => void
}
const VarItem: FC<ItemProps> = ({
readonly,
name,
label,
required,
type,
onEdit,
onRemove,
}) => {
const [isDeleting, setIsDeleting] = useState(false)
return (
<div className={cn('group relative flex items-center mb-1 last-of-type:mb-0 pl-2.5 py-2 pr-3 w-full rounded-lg bg-components-panel-on-panel-item-bg border-components-panel-border-subtle border-[0.5px] shadow-xs hover:shadow-sm hover:bg-components-panel-on-panel-item-bg-hover', isDeleting && 'hover:bg-state-destructive-hover border-state-destructive-border', readonly && 'cursor-not-allowed opacity-30')}>
<VarIcon className='shrink-0 mr-1 w-4 h-4 text-text-accent' />
<div className='grow'>
<div className='flex items-center h-[18px]'>
<div className='grow truncate' title={name}>
<span className='system-sm-medium text-text-secondary'>{name}</span>
<span className='px-1 system-xs-regular text-text-quaternary'>·</span>
<span className='system-xs-medium text-text-tertiary'>{label}</span>
</div>
<div className='group-hover:hidden flex items-center'>
{required && <Badge text='required' />}
<span className='pl-2 pr-1 system-xs-regular text-text-tertiary'>{type}</span>
<IconTypeIcon type={type as IInputTypeIconProps['type']} className='text-text-tertiary' />
</div>
</div>
</div>
{!readonly && (
<div className='hidden rounded-lg group-hover:flex items-center justify-end absolute right-0 top-0 bottom-0 pr-2 w-[124px]'>
<div
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
onClick={onEdit}
>
<RiEditLine className='w-4 h-4 text-text-tertiary' />
</div>
<div
className='flex items-center justify-center w-6 h-6 text-text-tertiary cursor-pointer hover:text-text-destructive'
onClick={onRemove}
onMouseOver={() => setIsDeleting(true)}
onMouseLeave={() => setIsDeleting(false)}
>
<RiDeleteBinLine className='w-4 h-4' />
</div>
</div>
)}
</div>
)
}
export default VarItem

View file

@ -57,7 +57,7 @@ const ConfigVision: FC = () => {
return null
return (
<div className='mt-2 flex items-center gap-2 p-2 rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn'>
<div className='mt-2 flex items-center gap-2 p-2 rounded-xl border-effects-highlight border-t-[0.5px] border-l-[0.5px] bg-background-section-burn'>
<div className='shrink-0 p-1'>
<div className='p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-indigo-indigo-600'>
<Vision className='w-4 h-4 text-text-primary-on-surface' />
@ -99,7 +99,7 @@ const ConfigVision: FC = () => {
/>
</div> */}
<ParamConfig />
<div className='ml-1 mr-3 w-[1px] h-3.5 bg-divider-subtle'></div>
<div className='ml-1 mr-3 w-[1px] h-3.5 bg-divider-regular'></div>
<Switch
defaultValue={isImageEnabled}
onChange={handleChange}

View file

@ -41,11 +41,11 @@ const ParamConfigContent: FC = () => {
return (
<div>
<div className='leading-6 text-base font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.title')}</div>
<div className='leading-6 text-base font-semibold text-text-primary'>{t('appDebug.vision.visionSettings.title')}</div>
<div className='pt-3 space-y-6'>
<div>
<div className='mb-2 flex items-center space-x-1'>
<div className='leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.resolution')}</div>
<div className='leading-[18px] text-[13px] font-semibold text-text-secondary'>{t('appDebug.vision.visionSettings.resolution')}</div>
<Tooltip
popupContent={
<div className='w-[180px]' >
@ -78,7 +78,7 @@ const ParamConfigContent: FC = () => {
</div>
</div>
<div>
<div className='mb-2 leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.uploadMethod')}</div>
<div className='mb-2 leading-[18px] text-[13px] font-semibold text-text-secondary'>{t('appDebug.vision.visionSettings.uploadMethod')}</div>
<div className='flex items-center gap-1'>
<OptionCard
className='grow'

View file

@ -2,14 +2,15 @@
import type { FC } from 'react'
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiSettings2Line } from '@remixicon/react'
import ParamConfigContent from './param-config-content'
import cn from '@/utils/classnames'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import Button from '@/app/components/base/button'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import cn from '@/utils/classnames'
const ParamsConfig: FC = () => {
const { t } = useTranslation()
@ -25,13 +26,13 @@ const ParamsConfig: FC = () => {
}}
>
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
<div className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-text-tertiary cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
<Settings01 className='w-3.5 h-3.5 ' />
<div className='ml-1 leading-[18px] text-xs font-medium '>{t('appDebug.voice.settings')}</div>
</div>
<Button variant='ghost' size='small' className={cn('')}>
<RiSettings2Line className='w-3.5 h-3.5' />
<div className='ml-1'>{t('appDebug.voice.settings')}</div>
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 50 }}>
<div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
<div className='w-80 sm:w-[412px] p-4 bg-components-panel-bg rounded-lg border-[0.5px] border-components-panel-border shadow-lg space-y-3'>
<ParamConfigContent />
</div>
</PortalToFollowElemContent>

View file

@ -2,9 +2,9 @@
import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiSettings2Line } from '@remixicon/react'
import AgentSetting from './agent/agent-setting'
import Button from '@/app/components/base/button'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import type { AgentConfig } from '@/models/debug'
type Props = {
@ -26,7 +26,7 @@ const AgentSettingButton: FC<Props> = ({
return (
<>
<Button onClick={() => setIsShowAgentSetting(true)} className='shrink-0 mr-2'>
<Settings01 className='mr-1 w-4 h-4 text-gray-500' />
<RiSettings2Line className='mr-1 w-4 h-4 text-text-tertiary' />
{t('appDebug.agent.setting.name')}
</Button>
{isShowAgentSetting && (

View file

@ -42,10 +42,10 @@ const AgentSetting: FC<Props> = ({
}}
>
<div
className='w-[640px] flex flex-col h-full overflow-hidden bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl'
className='w-[640px] flex flex-col h-full overflow-hidden bg-components-panel-bg border-[0.5px] border-components-panel-border rounded-xl shadow-xl'
>
<div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-b-gray-100'>
<div className='flex flex-col text-base font-semibold text-gray-900'>
<div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-divider-regular'>
<div className='flex flex-col text-base font-semibold text-text-primary'>
<div className='leading-6'>{t('appDebug.agent.setting.name')}</div>
</div>
<div className='flex items-center'>
@ -53,7 +53,7 @@ const AgentSetting: FC<Props> = ({
onClick={onCancel}
className='flex justify-center items-center w-6 h-6 cursor-pointer'
>
<RiCloseLine className='w-4 h-4 text-gray-500' />
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
</div>
</div>
</div>
@ -70,7 +70,7 @@ const AgentSetting: FC<Props> = ({
name={t('appDebug.agent.agentMode')}
description={t('appDebug.agent.agentModeDes')}
>
<div className='leading-[18px] text-[13px] font-medium text-gray-900'>{isFunctionCall ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</div>
<div className='leading-[18px] text-[13px] font-medium text-text-primary'>{isFunctionCall ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</div>
</ItemPanel>
<ItemPanel
@ -99,10 +99,10 @@ const AgentSetting: FC<Props> = ({
type="number"
min={maxIterationsMin}
max={maxIterationsMax} step={1}
className="block w-11 h-7 leading-7 rounded-lg border-0 pl-1 px-1.5 bg-gray-100 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600"
className="block w-11 h-7 leading-7 rounded-lg border-0 pl-1 px-1.5 bg-components-input-bg-normal text-text-primary placeholder:text-text-tertiary focus:ring-1 focus:ring-inset focus:ring-primary-600"
value={tempPayload.max_iteration}
onChange={(e) => {
let value = parseInt(e.target.value, 10)
let value = Number.parseInt(e.target.value, 10)
if (value < maxIterationsMin)
value = maxIterationsMin
@ -117,23 +117,20 @@ const AgentSetting: FC<Props> = ({
</ItemPanel>
{!isFunctionCall && (
<div className='py-2 bg-gray-50 rounded-xl shadow-xs'>
<div className='flex items-center h-8 px-4 leading-6 text-sm font-semibold text-gray-700'>{t('tools.builtInPromptTitle')}</div>
<div className='h-[396px] px-4 overflow-y-auto leading-5 text-sm font-normal text-gray-700 whitespace-pre-line'>
<div className='py-2 bg-background-section-burn rounded-xl shadow-xs'>
<div className='flex items-center h-8 px-4 leading-6 text-sm font-semibold text-text-secondary'>{t('tools.builtInPromptTitle')}</div>
<div className='h-[396px] px-4 overflow-y-auto leading-5 text-sm font-normal text-text-secondary whitespace-pre-line'>
{isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion}
</div>
<div className='px-4'>
<div className='inline-flex items-center h-5 px-1 rounded-md bg-gray-100 leading-[18px] text-xs font-medium text-gray-500'>{(isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion).length}</div>
<div className='inline-flex items-center h-5 px-1 bg-components-input-bg-normal rounded-md leading-[18px] text-xs font-medium text-text-tertiary'>{(isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion).length}</div>
</div>
</div>
)}
</div>
<div
className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
style={{
borderColor: 'rgba(0, 0, 0, 0.05)',
}}
className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-background-section-burn border-divider-regular'
>
<Button
onClick={onCancel}

View file

@ -19,10 +19,10 @@ const ItemPanel: FC<Props> = ({
children,
}) => {
return (
<div className={cn(className, 'flex justify-between items-center h-12 px-3 rounded-lg bg-gray-50')}>
<div className={cn(className, 'flex justify-between items-center h-12 px-3 rounded-lg bg-background-section-burn')}>
<div className='flex items-center'>
{icon}
<div className='ml-3 mr-1 leading-6 text-sm font-semibold text-gray-800'>{name}</div>
<div className='ml-3 mr-1 leading-6 text-sm font-semibold text-text-secondary'>{name}</div>
<Tooltip
popupContent={
<div className='w-[180px]'>

View file

@ -1,21 +1,24 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import React, { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import copy from 'copy-to-clipboard'
import produce from 'immer'
import {
RiDeleteBinLine,
RiHammerFill,
RiEqualizer2Line,
RiInformation2Line,
} from '@remixicon/react'
import { useFormattingChangedDispatcher } from '../../../debug/hooks'
import SettingBuiltInTool from './setting-built-in-tool'
import cn from '@/utils/classnames'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
import AppIcon from '@/app/components/base/app-icon'
import Button from '@/app/components/base/button'
import Indicator from '@/app/components/header/indicator'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import ConfigContext from '@/context/debug-configuration'
import type { AgentTool } from '@/types/app'
import { type Collection, CollectionType } from '@/app/components/tools/types'
@ -23,7 +26,12 @@ import { MAX_TOOLS_NUM } from '@/config'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import Tooltip from '@/app/components/base/tooltip'
import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other'
import AddToolModal from '@/app/components/tools/add-tool-modal'
// import AddToolModal from '@/app/components/tools/add-tool-modal'
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
import { updateBuiltInToolCredential } from '@/service/tools'
import cn from '@/utils/classnames'
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
type AgentToolWithMoreInfo = AgentTool & { icon: any; collection?: Collection } | null
const AgentTools: FC = () => {
@ -33,9 +41,19 @@ const AgentTools: FC = () => {
const formattingChangedDispatcher = useFormattingChangedDispatcher()
const [currentTool, setCurrentTool] = useState<AgentToolWithMoreInfo>(null)
const currentCollection = useMemo(() => {
if (!currentTool) return null
const collection = collectionList.find(collection => collection.id.split('/').pop() === currentTool?.provider_id.split('/').pop() && collection.type === currentTool?.provider_type)
return collection
}, [currentTool, collectionList])
const [isShowSettingTool, setIsShowSettingTool] = useState(false)
const [isShowSettingAuth, setShowSettingAuth] = useState(false)
const tools = (modelConfig?.agentConfig?.tools as AgentTool[] || []).map((item) => {
const collection = collectionList.find(collection => collection.id === item.provider_id && collection.type === item.provider_type)
const collection = collectionList.find(
collection =>
collection.id.split('/').pop() === item.provider_id.split('/').pop()
&& collection.type === item.provider_type,
)
const icon = collection?.icon
return {
...item,
@ -55,14 +73,40 @@ const AgentTools: FC = () => {
formattingChangedDispatcher()
}
const handleToolAuthSetting = (value: any) => {
const newModelConfig = produce(modelConfig, (draft) => {
const tool = (draft.agentConfig.tools).find((item: any) => item.provider_id === value?.collection?.id && item.tool_name === value?.tool_name)
if (tool)
(tool as AgentTool).notAuthor = false
})
setModelConfig(newModelConfig)
setIsShowSettingTool(false)
formattingChangedDispatcher()
}
const [isDeleting, setIsDeleting] = useState<number>(-1)
const handleSelectTool = (tool: ToolDefaultValue) => {
const newModelConfig = produce(modelConfig, (draft) => {
draft.agentConfig.tools.push({
provider_id: tool.provider_id,
provider_type: tool.provider_type as CollectionType,
provider_name: tool.provider_name,
tool_name: tool.tool_name,
tool_label: tool.tool_label,
tool_parameters: tool.params,
notAuthor: !tool.is_team_authorization,
enabled: true,
})
})
setModelConfig(newModelConfig)
}
return (
<>
<Panel
className="mt-2"
className={cn('mt-2', tools.length === 0 && 'pb-2')}
noBodySpacing={tools.length === 0}
headerIcon={
<RiHammerFill className='w-4 h-4 text-primary-500' />
}
title={
<div className='flex items-center'>
<div className='mr-1'>{t('appDebug.agent.tools.name')}</div>
@ -77,11 +121,19 @@ const AgentTools: FC = () => {
}
headerRight={
<div className='flex items-center'>
<div className='leading-[18px] text-xs font-normal text-gray-500'>{tools.filter((item: any) => !!item.enabled).length}/{tools.length}&nbsp;{t('appDebug.agent.tools.enabled')}</div>
<div className='leading-[18px] text-xs font-normal text-text-tertiary'>{tools.filter((item: any) => !!item.enabled).length}/{tools.length}&nbsp;{t('appDebug.agent.tools.enabled')}</div>
{tools.length < MAX_TOOLS_NUM && (
<>
<div className='ml-3 mr-1 h-3.5 w-px bg-gray-200'></div>
<OperationBtn type="add" onClick={() => setIsShowChooseTool(true)} />
<div className='ml-3 mr-1 h-3.5 w-px bg-divider-regular'></div>
<ToolPicker
trigger={<OperationBtn type="add" />}
isShow={isShowChooseTool}
onShowChange={setIsShowChooseTool}
disabled={false}
supportAddCustomTool
onSelect={handleSelectTool}
selectedTools={tools}
/>
</>
)}
</div>
@ -90,72 +142,77 @@ const AgentTools: FC = () => {
<div className='grid gap-1 grid-cols-1 2xl:grid-cols-2 items-center flex-wrap justify-between'>
{tools.map((item: AgentTool & { icon: any; collection?: Collection }, index) => (
<div key={index}
className={cn((item.isDeleted || item.notAuthor) ? 'bg-white/50' : 'bg-white', (item.enabled && !item.isDeleted && !item.notAuthor) && 'shadow-xs', index > 1 && 'mt-1', 'group relative flex justify-between items-center last-of-type:mb-0 pl-2.5 py-2 pr-3 w-full rounded-lg border-[0.5px] border-gray-200 ')}
className={cn(
'group relative flex justify-between items-center last-of-type:mb-0 p-1.5 pr-2 w-full bg-components-panel-on-panel-item-bg rounded-lg border-[0.5px] border-components-panel-border-subtle shadow-xs hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm cursor',
isDeleting === index && 'hover:bg-state-destructive-hover border-state-destructive-border',
)}
>
<div className='grow w-0 flex items-center'>
{(item.isDeleted || item.notAuthor)
? (
<DefaultToolIcon className='w-6 h-6' />
)
: (
typeof item.icon === 'string'
? (
<div
className='w-6 h-6 bg-cover bg-center rounded-md'
style={{
backgroundImage: `url(${item.icon})`,
}}
></div>
)
: (
<AppIcon
className='rounded-md'
size='tiny'
icon={item.icon?.content}
background={item.icon?.background}
/>
))}
{item.isDeleted && <DefaultToolIcon className='w-5 h-5' />}
{!item.isDeleted && (
<div className={cn((item.notAuthor || !item.enabled) && 'opacity-50')}>
{typeof item.icon === 'string' && <div className='w-5 h-5 bg-cover bg-center rounded-md' style={{ backgroundImage: `url(${item.icon})` }} />}
{typeof item.icon !== 'string' && <AppIcon className='rounded-md' size='xs' icon={item.icon?.content} background={item.icon?.background} />}
</div>
)}
<div
className={cn((item.isDeleted || item.notAuthor) ? 'line-through opacity-50' : '', 'grow w-0 ml-2 leading-[18px] text-[13px] font-medium text-gray-800 truncate')}
className={cn(
'grow w-0 ml-1.5 flex items-center system-xs-regular truncate',
(item.isDeleted || item.notAuthor || !item.enabled) ? 'opacity-50' : '',
)}
>
<span className='text-gray-800 pr-2'>{item.provider_type === CollectionType.builtIn ? item.provider_name : item.tool_label}</span>
<Tooltip
popupContent={t('tools.toolNameUsageTip')}
>
<span className='text-gray-500'>{item.tool_name}</span>
</Tooltip>
<span className='text-text-secondary system-xs-medium pr-1.5'>{item.provider_type === CollectionType.builtIn ? item.provider_name.split('/').pop() : item.tool_label}</span>
<span className='text-text-tertiary'>{item.tool_label}</span>
{!item.isDeleted && (
<Tooltip
needsDelay
popupContent={
<div className='w-[180px]'>
<div className='mb-1.5 text-text-secondary'>{item.tool_name}</div>
<div className='mb-1.5 text-text-tertiary'>{t('tools.toolNameUsageTip')}</div>
<div className='text-text-accent cursor-pointer' onClick={() => copy(item.tool_name)}>{t('tools.copyToolName')}</div>
</div>
}
>
<div className='w-4 h-4'>
<div className='hidden group-hover:inline-block ml-0.5'>
<RiInformation2Line className='w-4 h-4 text-text-tertiary' />
</div>
</div>
</Tooltip>
)}
</div>
</div>
<div className='shrink-0 ml-1 flex items-center'>
{(item.isDeleted || item.notAuthor)
? (
<div className='flex items-center'>
<Tooltip
popupContent={t(`tools.${item.isDeleted ? 'toolRemoved' : 'notAuthorized'}`)}
needsDelay
>
<div className='mr-1 p-1 rounded-md hover:bg-black/5 cursor-pointer' onClick={() => {
if (item.notAuthor)
setIsShowChooseTool(true)
}}>
<AlertTriangle className='w-4 h-4 text-[#F79009]' />
</div>
</Tooltip>
<div className='p-1 rounded-md hover:bg-black/5 cursor-pointer' onClick={() => {
{item.isDeleted && (
<div className='flex items-center mr-2'>
<Tooltip
popupContent={t('tools.toolRemoved')}
needsDelay
>
<div className='mr-1 p-1 rounded-md hover:bg-black/5 cursor-pointer'>
<AlertTriangle className='w-4 h-4 text-[#F79009]' />
</div>
</Tooltip>
<div
className='p-1 rounded-md text-text-tertiary cursor-pointer hover:text-text-destructive'
onClick={() => {
const newModelConfig = produce(modelConfig, (draft) => {
draft.agentConfig.tools.splice(index, 1)
})
setModelConfig(newModelConfig)
formattingChangedDispatcher()
}}>
<RiDeleteBinLine className='w-4 h-4 text-gray-500' />
</div>
<div className='ml-2 mr-3 w-px h-3.5 bg-gray-200'></div>
}}
onMouseOver={() => setIsDeleting(index)}
onMouseLeave={() => setIsDeleting(-1)}
>
<RiDeleteBinLine className='w-4 h-4' />
</div>
)
: (
<div className='hidden group-hover:flex items-center'>
</div>
)}
{!item.isDeleted && (
<div className='hidden group-hover:flex items-center gap-1 mr-2'>
{!item.notAuthor && (
<Tooltip
popupContent={t('tools.setBuiltInTools.infoAndSetting')}
needsDelay
@ -164,55 +221,81 @@ const AgentTools: FC = () => {
setCurrentTool(item)
setIsShowSettingTool(true)
}}>
<InfoCircle className='w-4 h-4 text-gray-500' />
<RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
</div>
</Tooltip>
<div className='p-1 rounded-md hover:bg-black/5 cursor-pointer' onClick={() => {
)}
<div
className='p-1 rounded-md text-text-tertiary cursor-pointer hover:text-text-destructive'
onClick={() => {
const newModelConfig = produce(modelConfig, (draft) => {
draft.agentConfig.tools.splice(index, 1)
})
setModelConfig(newModelConfig)
formattingChangedDispatcher()
}}>
<RiDeleteBinLine className='w-4 h-4 text-gray-500' />
</div>
<div className='ml-2 mr-3 w-px h-3.5 bg-gray-200'></div>
}}
onMouseOver={() => setIsDeleting(index)}
onMouseLeave={() => setIsDeleting(-1)}
>
<RiDeleteBinLine className='w-4 h-4' />
</div>
</div>
)}
<div className={cn(item.isDeleted && 'opacity-50')}>
{!item.notAuthor && (
<Switch
defaultValue={item.isDeleted ? false : item.enabled}
disabled={item.isDeleted}
size='md'
onChange={(enabled) => {
const newModelConfig = produce(modelConfig, (draft) => {
(draft.agentConfig.tools[index] as any).enabled = enabled
})
setModelConfig(newModelConfig)
formattingChangedDispatcher()
}} />
)}
{item.notAuthor && (
<Button variant='secondary' size='small' onClick={() => {
setCurrentTool(item)
setShowSettingAuth(true)
}}>
{t('tools.notAuthorized')}
<Indicator className='ml-2' color='orange' />
</Button>
)}
<div className={cn((item.isDeleted || item.notAuthor) && 'opacity-50')}>
<Switch
defaultValue={(item.isDeleted || item.notAuthor) ? false : item.enabled}
disabled={(item.isDeleted || item.notAuthor)}
size='md'
onChange={(enabled) => {
const newModelConfig = produce(modelConfig, (draft) => {
(draft.agentConfig.tools[index] as any).enabled = enabled
})
setModelConfig(newModelConfig)
formattingChangedDispatcher()
}} />
</div>
</div>
</div>
))}
</div >
</Panel >
{isShowChooseTool && (
<AddToolModal onHide={() => setIsShowChooseTool(false)} />
{isShowSettingTool && (
<SettingBuiltInTool
toolName={currentTool?.tool_name as string}
setting={currentTool?.tool_parameters as any}
collection={currentTool?.collection as Collection}
isBuiltIn={currentTool?.collection?.type === CollectionType.builtIn}
isModel={currentTool?.collection?.type === CollectionType.model}
onSave={handleToolSettingChange}
onHide={() => setIsShowSettingTool(false)}
/>
)}
{isShowSettingAuth && (
<ConfigCredential
collection={currentCollection as any}
onCancel={() => setShowSettingAuth(false)}
onSaved={async (value) => {
await updateBuiltInToolCredential((currentCollection as any).name, value)
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
handleToolAuthSetting(currentTool as any)
setShowSettingAuth(false)
}}
/>
)}
{
isShowSettingTool && (
<SettingBuiltInTool
toolName={currentTool?.tool_name as string}
setting={currentTool?.tool_parameters as any}
collection={currentTool?.collection as Collection}
isBuiltIn={currentTool?.collection?.type === CollectionType.builtIn}
isModel={currentTool?.collection?.type === CollectionType.model}
onSave={handleToolSettingChange}
onHide={() => setIsShowSettingTool(false)}
/>)
}
</>
)
}

View file

@ -3,21 +3,30 @@ import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import cn from '@/utils/classnames'
import Drawer from '@/app/components/base/drawer-plus'
import {
RiArrowLeftLine,
RiCloseLine,
} from '@remixicon/react'
import Drawer from '@/app/components/base/drawer'
import Loading from '@/app/components/base/loading'
import ActionButton from '@/app/components/base/action-button'
import Icon from '@/app/components/plugins/card/base/card-icon'
import OrgInfo from '@/app/components/plugins/card/base/org-info'
import Description from '@/app/components/plugins/card/base/description'
import TabSlider from '@/app/components/base/tab-slider-plain'
import Button from '@/app/components/base/button'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import type { Collection, Tool } from '@/app/components/tools/types'
import { CollectionType } from '@/app/components/tools/types'
import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList, fetchWorkflowToolList } from '@/service/tools'
import I18n from '@/context/i18n'
import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading'
import { DiagonalDividingLine } from '@/app/components/base/icons/src/public/common'
import { getLanguage } from '@/i18n/language'
import AppIcon from '@/app/components/base/app-icon'
import cn from '@/utils/classnames'
type Props = {
showBackButton?: boolean
collection: Collection
isBuiltIn?: boolean
isModel?: boolean
@ -29,6 +38,7 @@ type Props = {
}
const SettingBuiltInTool: FC<Props> = ({
showBackButton = false,
collection,
isBuiltIn = true,
isModel = true,
@ -96,39 +106,38 @@ const SettingBuiltInTool: FC<Props> = ({
return valid
})()
const infoUI = (
<div className='pt-2'>
<div className='leading-5 text-sm font-medium text-gray-900'>
{t('tools.setBuiltInTools.toolDescription')}
</div>
<div className='mt-1 leading-[18px] text-xs font-normal text-gray-600'>
{currTool?.description[language]}
</div>
const getType = (type: string) => {
if (type === 'number-input')
return t('tools.setBuiltInTools.number')
if (type === 'text-input')
return t('tools.setBuiltInTools.string')
if (type === 'file')
return t('tools.setBuiltInTools.file')
return type
}
const infoUI = (
<div className=''>
{infoSchemas.length > 0 && (
<div className='mt-6'>
<div className='flex items-center mb-4 leading-[18px] text-xs font-semibold text-gray-500 uppercase'>
<div className='mr-3'>{t('tools.setBuiltInTools.parameters')}</div>
<div className='grow w-0 h-px bg-[#f3f4f6]'></div>
</div>
<div className='space-y-4'>
{infoSchemas.map((item: any, index) => (
<div key={index}>
<div className='flex items-center space-x-2 leading-[18px]'>
<div className='text-[13px] font-semibold text-gray-900'>{item.label[language]}</div>
<div className='text-xs font-medium text-gray-500'>{item.type === 'number-input' ? t('tools.setBuiltInTools.number') : t('tools.setBuiltInTools.string')}</div>
{item.required && (
<div className='text-xs font-medium text-[#EC4A0A]'>{t('tools.setBuiltInTools.required')}</div>
)}
<div className='py-2 space-y-1'>
{infoSchemas.map((item: any, index) => (
<div key={index} className='py-1'>
<div className='flex items-center gap-2'>
<div className='text-text-secondary code-sm-semibold'>{item.label[language]}</div>
<div className='text-text-tertiary system-xs-regular'>
{getType(item.type)}
</div>
{item.human_description && (
<div className='mt-1 leading-[18px] text-xs font-normal text-gray-600'>
{item.human_description?.[language]}
</div>
{item.required && (
<div className='text-text-warning-secondary system-xs-medium'>{t('tools.setBuiltInTools.required')}</div>
)}
</div>
))}
</div>
{item.human_description && (
<div className='mt-0.5 text-text-tertiary system-xs-regular'>
{item.human_description?.[language]}
</div>
)}
</div>
))}
</div>
)}
</div>
@ -142,82 +151,88 @@ const SettingBuiltInTool: FC<Props> = ({
isEditMode={false}
showOnVariableMap={{}}
validating={false}
inputClassName='!bg-gray-50'
readonly={readonly}
/>
)
return (
<Drawer
isShow
onHide={onHide}
title={(
<div className='flex items-center'>
{typeof collection.icon === 'string'
? (
<div
className='w-6 h-6 bg-cover bg-center rounded-md flex-shrink-0'
style={{
backgroundImage: `url(${collection.icon})`,
}}
></div>
)
: (
<AppIcon
className='rounded-md'
size='tiny'
icon={(collection.icon as any)?.content}
background={(collection.icon as any)?.background}
/>
)}
<div className='ml-2 leading-6 text-base font-semibold text-gray-900'>{currTool?.label[language]}</div>
{(hasSetting && !readonly) && (<>
<DiagonalDividingLine className='mx-4' />
<div className='flex space-x-6'>
<div
className={cn(isInfoActive ? 'text-gray-900 font-semibold' : 'font-normal text-gray-600 cursor-pointer', 'relative text-base')}
onClick={() => setCurrType('info')}
>
{t('tools.setBuiltInTools.info')}
{isInfoActive && <div className='absolute left-0 bottom-[-16px] w-full h-0.5 bg-primary-600'></div>}
isOpen
clickOutsideNotOpen={false}
onClose={onHide}
footer={null}
mask={false}
positionCenter={false}
panelClassname={cn('justify-start mt-[64px] mr-2 mb-2 !w-[420px] !max-w-[420px] !p-0 !bg-components-panel-bg rounded-2xl border-[0.5px] border-components-panel-border shadow-xl')}
>
<>
{isLoading && <Loading type='app' />}
{!isLoading && (
<>
{/* header */}
<div className='relative p-4 pb-3 border-b border-divider-subtle'>
<div className='absolute top-3 right-3'>
<ActionButton onClick={onHide}>
<RiCloseLine className='w-4 h-4' />
</ActionButton>
</div>
<div className={cn(!isInfoActive ? 'text-gray-900 font-semibold' : 'font-normal text-gray-600 cursor-pointer', 'relative text-base ')}
onClick={() => setCurrType('setting')}
>
{t('tools.setBuiltInTools.setting')}
{!isInfoActive && <div className='absolute left-0 bottom-[-16px] w-full h-0.5 bg-primary-600'></div>}
</div>
</div>
</>)}
</div>
)}
panelClassName='mt-[65px] !w-[405px]'
maxWidthClassName='!max-w-[405px]'
height='calc(100vh - 65px)'
headerClassName='!border-b-black/5'
body={
<div className='h-full pt-3'>
{isLoading
? <div className='flex h-full items-center'>
<Loading type='app' />
</div>
: (<div className='flex flex-col h-full'>
<div className='grow h-0 overflow-y-auto px-6'>
{isInfoActive ? infoUI : settingUI}
</div>
{!readonly && !isInfoActive && (
<div className='mt-2 shrink-0 flex justify-end py-4 px-6 space-x-2 rounded-b-[10px] bg-gray-50 border-t border-black/5'>
<Button className='flex items-center h-8 !px-3 !text-[13px] font-medium !text-gray-700' onClick={onHide}>{t('common.operation.cancel')}</Button>
<Button className='flex items-center h-8 !px-3 !text-[13px] font-medium' variant='primary' disabled={!isValid} onClick={() => onSave?.(addDefaultValue(tempSetting, formSchemas))}>{t('common.operation.save')}</Button>
{showBackButton && (
<div
className='mb-2 flex items-center gap-1 text-text-accent-secondary system-xs-semibold-uppercase cursor-pointer'
onClick={onHide}
>
<RiArrowLeftLine className='w-4 h-4' />
BACK
</div>
)}
</div>)}
</div>
}
isShowMask={false}
clickOutsideNotOpen={false}
/>
<div className='flex items-center gap-1'>
<Icon size='tiny' className='w-6 h-6' src={collection.icon} />
<OrgInfo
packageNameClassName='w-auto'
orgName={collection.author}
packageName={collection.name.split('/').pop() || ''}
/>
</div>
<div className='mt-1 text-text-primary system-md-semibold'>{currTool?.label[language]}</div>
{!!currTool?.description[language] && (
<Description className='mt-3' text={currTool.description[language]} descriptionLineRows={2}></Description>
)}
</div>
{/* form */}
<div className='h-full'>
<div className='flex flex-col h-full'>
{(hasSetting && !readonly) ? (
<TabSlider
className='shrink-0 mt-1 px-4'
itemClassName='py-3'
noBorderBottom
value={currType}
onChange={(value) => {
setCurrType(value)
}}
options={[
{ value: 'info', text: t('tools.setBuiltInTools.parameters')! },
{ value: 'setting', text: t('tools.setBuiltInTools.setting')! },
]}
/>
) : (
<div className='p-4 pb-1 text-text-primary system-sm-semibold-uppercase'>{t('tools.setBuiltInTools.parameters')}</div>
)}
<div className='grow h-0 overflow-y-auto px-4'>
{isInfoActive ? infoUI : settingUI}
</div>
{!readonly && !isInfoActive && (
<div className='mt-2 shrink-0 flex justify-end py-4 px-6 space-x-2 rounded-b-[10px] bg-components-panel-bg border-t border-divider-regular'>
<Button className='flex items-center h-8 !px-3 !text-[13px] font-medium ' onClick={onHide}>{t('common.operation.cancel')}</Button>
<Button className='flex items-center h-8 !px-3 !text-[13px] font-medium' variant='primary' disabled={!isValid} onClick={() => onSave?.(addDefaultValue(tempSetting, formSchemas))}>{t('common.operation.save')}</Button>
</div>
)}
</div>
</div>
</>
)}
</>
</Drawer>
)
}
export default React.memo(SettingBuiltInTool)

View file

@ -38,7 +38,7 @@ import ModelName from '@/app/components/header/account-setting/model-provider-pa
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
export type IGetAutomaticResProps = {
export interface IGetAutomaticResProps {
mode: AppType
model: Model
isShow: boolean

View file

@ -12,7 +12,7 @@ import AgentTools from './agent/agent-tools'
import ConfigContext from '@/context/debug-configuration'
import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
import ConfigVar from '@/app/components/app/configuration/config-var'
import { type ModelConfig, type PromptVariable } from '@/models/debug'
import type { ModelConfig, PromptVariable } from '@/models/debug'
import type { AppType } from '@/types/app'
import { ModelModeType } from '@/types/app'

View file

@ -16,6 +16,7 @@ import Drawer from '@/app/components/base/drawer'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames'
type ItemProps = {
className?: string
@ -43,8 +44,10 @@ const Item: FC<ItemProps> = ({
setShowSettingsModal(false)
}
const [isDeleting, setIsDeleting] = useState(false)
return (
<div className='group relative flex items-center mb-1 last-of-type:mb-0 pl-2.5 py-2 pr-3 w-full bg-white rounded-lg border-[0.5px] border-gray-200 shadow-xs'>
<div className={cn('group relative flex items-center mb-1 last-of-type:mb-0 pl-2.5 py-2 pr-3 w-full rounded-lg bg-components-panel-on-panel-item-bg border-components-panel-border-subtle border-[0.5px] shadow-xs hover:shadow-sm hover:bg-components-panel-on-panel-item-bg-hover', isDeleting && 'hover:bg-state-destructive-hover border-state-destructive-border')}>
{
config.data_source_type === DataSourceType.FILE && (
<div className='shrink-0 flex items-center justify-center mr-2 w-6 h-6 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]'>
@ -68,28 +71,30 @@ const Item: FC<ItemProps> = ({
}
<div className='grow'>
<div className='flex items-center h-[18px]'>
<div className='grow text-[13px] font-medium text-gray-800 truncate' title={config.name}>{config.name}</div>
<div className='grow text-[13px] font-medium text-text-secondary truncate' title={config.name}>{config.name}</div>
{config.provider === 'external'
? <Badge text={t('dataset.externalTag') as string} />
: <Badge
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
/>}
</div>
</div>
</div >
<div className='hidden rounded-lg group-hover:flex items-center justify-end absolute right-0 top-0 bottom-0 pr-2 w-[124px] bg-gradient-to-r from-white/50 to-white to-50%'>
{
editable && <div
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
onClick={() => setShowSettingsModal(true)}
>
<RiEditLine className='w-4 h-4 text-gray-500' />
<RiEditLine className='w-4 h-4 text-text-tertiary' />
</div>
}
<div
className='group/action flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
className='flex items-center justify-center w-6 h-6 text-text-tertiary cursor-pointer hover:text-text-destructive'
onClick={() => onRemove(config.id)}
onMouseOver={() => setIsDeleting(true)}
onMouseLeave={() => setIsDeleting(false)}
>
<RiDeleteBinLine className='w-4 h-4 text-gray-500 group-hover/action:text-[#D92D20]' />
<RiDeleteBinLine className='w-4 h-4' />
</div>
</div>
<Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
@ -99,7 +104,7 @@ const Item: FC<ItemProps> = ({
onSave={handleSave}
/>
</Drawer>
</div>
</div >
)
}

View file

@ -14,12 +14,12 @@ const ContextVar: FC<Props> = (props) => {
const currItem = options.find(item => item.value === value)
const notSetVar = !currItem
return (
<div className={cn(notSetVar ? 'rounded-bl-xl rounded-br-xl bg-[#FEF0C7] border-[#FEF0C7]' : 'border-gray-200', 'flex justify-between items-center h-12 px-3 border-t ')}>
<div className={cn(notSetVar ? 'rounded-bl-xl rounded-br-xl bg-[#FEF0C7] border-[#FEF0C7]' : 'border-components-panel-border-subtle', 'flex justify-between items-center h-12 px-3 border-t ')}>
<div className='flex items-center space-x-1 shrink-0'>
<div className='p-1'>
<BracketsX className='w-4 h-4 text-primary-500' />
<BracketsX className='w-4 h-4 text-text-accent' />
</div>
<div className='mr-1 text-sm font-medium text-gray-800'>{t('appDebug.feature.dataSet.queryVariable.title')}</div>
<div className='mr-1 text-sm font-medium text-text-secondary'>{t('appDebug.feature.dataSet.queryVariable.title')}</div>
<Tooltip
popupContent={
<div className='w-[180px]'>

View file

@ -1,3 +0,0 @@
.trigger:hover .dropdownIcon {
color: #98A2B3;
}

View file

@ -3,7 +3,6 @@ import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ChevronDownIcon } from '@heroicons/react/24/outline'
import s from './style.module.css'
import cn from '@/utils/classnames'
import {
PortalToFollowElem,
@ -56,10 +55,9 @@ const VarPicker: FC<Props> = ({
>
<PortalToFollowElemTrigger className={cn(triggerClassName)} onClick={() => setOpen(v => !v)}>
<div className={cn(
s.trigger,
className,
notSetVar ? 'bg-[#FFFCF5] border-[#FEDF89] text-[#DC6803]' : ' hover:bg-gray-50 border-gray-200 text-primary-600',
open ? 'bg-gray-50' : 'bg-white',
notSetVar ? 'bg-[#FFFCF5] border-[#FEDF89] text-[#DC6803]' : ' hover:bg-components-button-secondary-bg border-components-button-secondary-border text-text-accent',
open ? 'bg-components-button-secondary-bg' : 'bg-transparent',
`
flex items-center h-8 justify-center px-2 space-x-1 rounded-lg border shadow-xs cursor-pointer
text-[13px] font-medium
@ -73,16 +71,16 @@ const VarPicker: FC<Props> = ({
{notSelectedVarTip || t('appDebug.feature.dataSet.queryVariable.choosePlaceholder')}
</div>)}
</div>
<ChevronDownIcon className={cn(s.dropdownIcon, open && 'rotate-180 text-[#98A2B3]', 'w-3.5 h-3.5')} />
<ChevronDownIcon className={cn(open && 'rotate-180 text-text-tertiary', 'w-3.5 h-3.5')} />
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 1000 }}>
{options.length > 0
? (<div className='w-[240px] max-h-[50vh] overflow-y-auto p-1 border bg-white border-gray-200 rounded-lg shadow-lg'>
? (<div className='w-[240px] max-h-[50vh] overflow-y-auto p-1 border bg-components-panel-bg border-components-panel-border rounded-lg shadow-lg'>
{options.map(({ name, value, type }, index) => (
<div
key={index}
className='px-3 py-1 flex rounded-lg hover:bg-gray-50 cursor-pointer'
className='px-3 py-1 flex rounded-lg hover:bg-state-base-hover cursor-pointer'
onClick={() => {
onChange(value)
setOpen(false)
@ -93,9 +91,9 @@ const VarPicker: FC<Props> = ({
))}
</div>)
: (
<div className='w-[240px] p-6 bg-white border border-gray-200 rounded-lg shadow-lg'>
<div className='mb-1 text-sm font-medium text-gray-700'>{t('appDebug.feature.dataSet.queryVariable.noVar')}</div>
<div className='text-xs leading-normal text-gray-500'>{t('appDebug.feature.dataSet.queryVariable.noVarTip')}</div>
<div className='w-[240px] p-6 bg-components-panel-bg border border-components-panel-border rounded-lg shadow-lg'>
<div className='mb-1 text-sm font-medium text-text-secondary'>{t('appDebug.feature.dataSet.queryVariable.noVar')}</div>
<div className='text-xs leading-normal text-text-tertiary'>{t('appDebug.feature.dataSet.queryVariable.noVarTip')}</div>
</div>
)}

View file

@ -22,13 +22,6 @@ import { ModelTypeEnum } from '@/app/components/header/account-setting/model-pro
import { useSelector as useAppContextSelector } from '@/context/app-context'
import { hasEditPermissionForDataset } from '@/utils/permission'
const Icon = (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M12.6667 5.34368C12.6667 5.32614 12.6667 5.31738 12.6659 5.30147C12.6502 4.97229 12.3607 4.68295 12.0315 4.66737C12.0156 4.66662 12.0104 4.66662 12 4.66663H9.8391C9.30248 4.66662 8.85957 4.66661 8.49878 4.69609C8.12405 4.72671 7.77958 4.79242 7.45603 4.95728C6.95426 5.21294 6.54631 5.62089 6.29065 6.12265C6.12579 6.44621 6.06008 6.79068 6.02946 7.16541C5.99999 7.5262 5.99999 7.96911 6 8.50574V15.4942C5.99999 16.0308 5.99999 16.4737 6.02946 16.8345C6.06008 17.2092 6.12579 17.5537 6.29065 17.8773C6.54631 18.379 6.95426 18.787 7.45603 19.0426C7.77958 19.2075 8.12405 19.2732 8.49878 19.3038C8.85958 19.3333 9.30248 19.3333 9.83912 19.3333H14.1609C14.6975 19.3333 15.1404 19.3333 15.5012 19.3038C15.8759 19.2732 16.2204 19.2075 16.544 19.0426C17.0457 18.787 17.4537 18.379 17.7093 17.8773C17.8742 17.5537 17.9399 17.2092 17.9705 16.8345C18 16.4737 18 16.0308 18 15.4942V10.6666C18 10.6562 18 10.6511 17.9993 10.6352C17.9837 10.306 17.6943 10.0164 17.3651 10.0007C17.3492 9.99997 17.3405 9.99997 17.323 9.99997L14.3787 9.99997C14.2105 9.99999 14.0466 10 13.9078 9.98867C13.7555 9.97622 13.5756 9.94684 13.3947 9.85464C13.1438 9.72681 12.9398 9.52284 12.812 9.27195C12.7198 9.09101 12.6904 8.91118 12.678 8.75879C12.6666 8.62001 12.6666 8.45615 12.6667 8.2879L12.6667 5.34368ZM9.33333 12.6666C8.96514 12.6666 8.66667 12.9651 8.66667 13.3333C8.66667 13.7015 8.96514 14 9.33333 14H14.6667C15.0349 14 15.3333 13.7015 15.3333 13.3333C15.3333 12.9651 15.0349 12.6666 14.6667 12.6666H9.33333ZM9.33333 15.3333C8.96514 15.3333 8.66667 15.6318 8.66667 16C8.66667 16.3681 8.96514 16.6666 9.33333 16.6666H13.3333C13.7015 16.6666 14 16.3681 14 16C14 15.6318 13.7015 15.3333 13.3333 15.3333H9.33333Z" fill="#6938EF" />
<path d="M16.6053 8.66662C16.8011 8.66662 16.8989 8.66663 16.9791 8.61747C17.0923 8.54806 17.16 8.38452 17.129 8.25538C17.107 8.16394 17.0432 8.10018 16.9155 7.97265L14.694 5.75111C14.5664 5.62345 14.5027 5.55962 14.4112 5.53764C14.2821 5.5066 14.1186 5.57429 14.0492 5.68752C14 5.7677 14 5.86557 14 6.06132L14 8.13327C14 8.31994 14 8.41328 14.0363 8.48459C14.0683 8.54731 14.1193 8.5983 14.182 8.63026C14.2533 8.66659 14.3466 8.66659 14.5333 8.66659L16.6053 8.66662Z" fill="#6938EF" />
</svg>
)
const DatasetConfig: FC = () => {
const { t } = useTranslation()
const userProfile = useAppContextSelector(s => s.userProfile)
@ -125,7 +118,6 @@ const DatasetConfig: FC = () => {
return (
<FeaturePanel
className='mt-2'
headerIcon={Icon}
title={t('appDebug.feature.dataSet.title')}
headerRight={
<div className='flex items-center gap-1'>
@ -152,7 +144,7 @@ const DatasetConfig: FC = () => {
)
: (
<div className='mt-1 px-3 pb-3'>
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.feature.dataSet.noData')}</div>
<div className='pt-2 pb-1 text-xs text-text-tertiary'>{t('appDebug.feature.dataSet.noData')}</div>
</div>
)}

View file

@ -24,6 +24,7 @@ import cn from '@/utils/classnames'
import { useSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowledge-retrieval/hooks'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import Divider from '@/app/components/base/divider'
type Props = {
datasetConfigs: DatasetConfigs
@ -188,7 +189,7 @@ const ConfigContent: FC<Props> = ({
<div className='shrink-0 mr-2 system-xs-semibold-uppercase text-text-secondary'>
{t('dataset.rerankSettings')}
</div>
<div className='grow h-[1px] bg-gradient-to-l from-white to-[rgba(16,24,40,0.08)]'></div>
<Divider bgStyle='gradient' className='!h-px mx-0' />
</div>
{
selectedDatasetsMode.inconsistentEmbeddingModel
@ -352,7 +353,7 @@ const ConfigContent: FC<Props> = ({
{isInWorkflow && type === RETRIEVE_TYPE.oneWay && (
<div className='mt-4'>
<div className='flex items-center space-x-0.5'>
<div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.systemReasoningModel.key')}</div>
<div className='leading-[32px] text-[13px] font-medium text-text-primary'>{t('common.modelProvider.systemReasoningModel.key')}</div>
<Tooltip
popupContent={t('common.modelProvider.systemReasoningModel.tip')}
/>

View file

@ -140,11 +140,11 @@ const ParamsConfig = ({
/>
<div className='mt-6 flex justify-end'>
<Button className='mr-2 flex-shrink-0' onClick={() => {
<Button className='mr-2 shrink-0' onClick={() => {
setTempDataSetConfigs(datasetConfigs)
setRerankSettingModalOpen(false)
}}>{t('common.operation.cancel')}</Button>
<Button variant='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
<Button variant='primary' className='shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
</div>
</Modal>
)

View file

@ -6,8 +6,6 @@ import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import produce from 'immer'
import TypeIcon from '../type-icon'
import s from './style.module.css'
import cn from '@/utils/classnames'
import Modal from '@/app/components/base/modal'
import type { DataSet } from '@/models/datasets'
import Button from '@/app/components/base/button'
@ -15,6 +13,7 @@ import { fetchDatasets } from '@/service/datasets'
import Loading from '@/app/components/base/loading'
import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames'
export type ISelectDataSetProps = {
isShow: boolean
@ -111,8 +110,8 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
borderColor: 'rgba(0, 0, 0, 0.02',
}}
>
<span className='text-gray-500'>{t('appDebug.feature.dataSet.noDataSet')}</span>
<Link href="/datasets/create" className='font-normal text-[#155EEF]'>{t('appDebug.feature.dataSet.toCreate')}</Link>
<span className='text-text-tertiary'>{t('appDebug.feature.dataSet.noDataSet')}</span>
<Link href="/datasets/create" className='font-normal text-text-accent'>{t('appDebug.feature.dataSet.toCreate')}</Link>
</div>
)}
@ -122,7 +121,11 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
{datasets.map(item => (
<div
key={item.id}
className={cn(s.item, selected.some(i => i.id === item.id) && s.selected, 'flex justify-between items-center h-10 px-2 rounded-lg bg-white border border-gray-200 cursor-pointer', !item.embedding_available && s.disabled)}
className={cn(
'flex justify-between items-center h-10 px-2 rounded-lg bg-components-panel-on-panel-item-bg border-components-panel-border-subtle border-[0.5px] shadow-xs cursor-pointer hover:border-components-panel-border hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm',
selected.some(i => i.id === item.id) && 'border-[1.5px] border-components-option-card-option-selected-border bg-state-accent-hover shadow-xs hover:shadow-xs hover:border-components-option-card-option-selected-border hover:bg-state-accent-hover',
!item.embedding_available && 'hover:border-components-panel-border-subtle hover:bg-components-panel-on-panel-item-bg hover:shadow-xs',
)}
onClick={() => {
if (!item.embedding_available)
return
@ -130,12 +133,12 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
}}
>
<div className='mr-1 flex items-center'>
<div className={cn('mr-2', !item.embedding_available && 'opacity-50')}>
<div className={cn('mr-2', !item.embedding_available && 'opacity-30')}>
<TypeIcon type="upload_file" size='md' />
</div>
<div className={cn('max-w-[200px] text-[13px] font-medium text-gray-800 overflow-hidden text-ellipsis whitespace-nowrap', !item.embedding_available && 'opacity-50 !max-w-[120px]')}>{item.name}</div>
<div className={cn('max-w-[200px] text-[13px] font-medium text-text-secondary overflow-hidden text-ellipsis whitespace-nowrap', !item.embedding_available && 'opacity-30 !max-w-[120px]')}>{item.name}</div>
{!item.embedding_available && (
<span className='ml-1 shrink-0 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
<span className='ml-1 shrink-0 px-1 border border-divider-deep rounded-md text-text-tertiary text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
)}
</div>
{
@ -157,7 +160,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
)}
{loaded && (
<div className='flex justify-between items-center mt-8'>
<div className='text-sm font-medium text-gray-700'>
<div className='text-sm font-medium text-text-secondary'>
{selected.length > 0 && `${selected.length} ${t('appDebug.feature.dataSet.selected')}`}
</div>
<div className='flex space-x-2'>

View file

@ -1,13 +0,0 @@
.item {
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
}
.item:hover,
.item.selected {
background: #F5F8FF;
border-color: #528BFF;
}
.item.disabled {
@apply bg-white border-gray-200 cursor-default;
}

View file

@ -172,14 +172,14 @@ const SettingsModal: FC<SettingsModalProps> = ({
return (
<div
className='overflow-hidden w-full flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl'
className='overflow-hidden w-full flex flex-col bg-components-panel-bg border-[0.5px] border-components-panel-border rounded-xl shadow-xl'
style={{
height: 'calc(100vh - 72px)',
}}
ref={ref}
>
<div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-b-gray-100'>
<div className='flex flex-col text-base font-semibold text-gray-900'>
<div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-divider-regular'>
<div className='flex flex-col text-base font-semibold text-text-primary'>
<div className='leading-6'>{t('datasetSettings.title')}</div>
</div>
<div className='flex items-center'>
@ -187,14 +187,12 @@ const SettingsModal: FC<SettingsModalProps> = ({
onClick={onCancel}
className='flex justify-center items-center w-6 h-6 cursor-pointer'
>
<RiCloseLine className='w-4 h-4 text-gray-500' />
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
</div>
</div>
</div>
{/* Body */}
<div className='p-6 pt-5 border-b overflow-y-auto pb-[68px]' style={{
borderBottom: 'rgba(0, 0, 0, 0.05)',
}}>
<div className='p-6 pt-5 border-b border-divider-regular overflow-y-auto pb-[68px]'>
<div className={cn(rowClass, 'items-center')}>
<div className={labelClass}>
<div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.name')}</div>
@ -217,7 +215,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
className='resize-none'
placeholder={t('datasetSettings.form.descPlaceholder') || ''}
/>
<a className='mt-2 flex items-center h-[18px] px-3 text-xs text-gray-500' href="https://docs.dify.ai/features/datasets#how-to-write-a-good-dataset-description" target='_blank' rel='noopener noreferrer'>
<a className='mt-2 flex items-center h-[18px] px-3 text-xs text-text-tertiary' href="https://docs.dify.ai/features/datasets#how-to-write-a-good-dataset-description" target='_blank' rel='noopener noreferrer'>
<BookOpenIcon className='w-3 h-[18px] mr-1' />
{t('datasetSettings.form.descWrite')}
</a>
@ -260,7 +258,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
<div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.embeddingModel')}</div>
</div>
<div className='w-full'>
<div className='w-full h-9 rounded-lg bg-gray-100 opacity-60'>
<div className='w-full h-8 rounded-lg bg-components-input-bg-normal opacity-60'>
<ModelSelector
readonly
defaultModel={{
@ -270,9 +268,9 @@ const SettingsModal: FC<SettingsModalProps> = ({
modelList={embeddingsModelList}
/>
</div>
<div className='mt-2 w-full text-xs leading-6 text-gray-500'>
<div className='mt-2 w-full text-xs leading-6 text-text-tertiary'>
{t('datasetSettings.form.embeddingModelTip')}
<span className='text-[#155eef] cursor-pointer' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
<span className='text-text-accent cursor-pointer' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
</div>
</div>
</div>
@ -326,8 +324,8 @@ const SettingsModal: FC<SettingsModalProps> = ({
<div className={cn(labelClass, 'w-auto min-w-[168px]')}>
<div>
<div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.retrievalSetting.title')}</div>
<div className='leading-[18px] text-xs font-normal text-gray-500'>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
<div className='leading-[18px] text-xs font-normal text-text-tertiary'>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
{t('datasetSettings.form.retrievalSetting.description')}
</div>
</div>
@ -360,16 +358,13 @@ const SettingsModal: FC<SettingsModalProps> = ({
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}}>
<RiCloseLine className='w-4 h-4 text-gray-500 ' />
<RiCloseLine className='w-4 h-4 text-gray-500' />
</div>
</div>
)}
<div
className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
style={{
borderColor: 'rgba(0, 0, 0, 0.05)',
}}
className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t border-divider-regular bg-background-section'
>
<Button
onClick={onCancel}

View file

@ -84,7 +84,6 @@ const ChatUserInput = ({
onSelect={(i) => { handleInputValueChange(key, i.value as string) }}
items={(options || []).map(i => ({ name: i, value: i }))}
allowSearch={false}
bgClassName='bg-gray-50'
/>
)}
{type === 'number' && (

View file

@ -64,11 +64,11 @@ const DebugItem: FC<DebugItemProps> = ({
return (
<div
className={`flex flex-col min-w-[320px] rounded-xl bg-white border-[0.5px] border-black/5 ${className}`}
className={`flex flex-col min-w-[320px] rounded-xl bg-background-section-burn ${className}`}
style={style}
>
<div className='shrink-0 flex items-center justify-between h-10 px-3 border-b-[0.5px] border-b-black/5'>
<div className='flex items-center justify-center w-6 h-5 font-medium italic text-gray-500'>
<div className='shrink-0 flex items-center justify-between h-10 px-3 border-b-[0.5px] border-divider-regular'>
<div className='flex items-center justify-center w-6 h-5 font-medium italic text-text-tertiary'>
#{index + 1}
</div>
<ModelParameterTrigger

View file

@ -73,7 +73,7 @@ const ModelParameterTrigger: FC<ModelParameterTriggerProps> = ({
<div
className={`
flex items-center max-w-[200px] h-8 px-2 rounded-lg cursor-pointer
${open && 'bg-gray-100'}
${open && 'bg-state-base-hover'}
${currentModel && currentModel.status !== ModelStatusEnum.active && '!bg-[#FFFAEB]'}
`}
>
@ -88,27 +88,27 @@ const ModelParameterTrigger: FC<ModelParameterTriggerProps> = ({
}
{
!currentProvider && (
<div className='flex items-center justify-center mr-1 w-4 h-4 rounded border border-dashed border-primary-100'>
<CubeOutline className='w-[11px] h-[11px] text-primary-600' />
<div className='flex items-center justify-center mr-1 w-4 h-4 rounded'>
<CubeOutline className='w-4 h-4 text-text-accent' />
</div>
)
}
{
currentModel && (
<ModelName
className='mr-0.5 text-gray-800'
className='mr-0.5 text-text-secondary'
modelItem={currentModel}
/>
)
}
{
!currentModel && (
<div className='mr-0.5 text-[13px] font-medium text-primary-600 truncate'>
<div className='mr-0.5 text-[13px] font-medium text-text-accent truncate'>
{t('common.modelProvider.selectModel')}
</div>
)
}
<RiArrowDownSLine className={`w-3 h-3 ${(currentModel && currentProvider) ? 'text-gray-800' : 'text-primary-600'}`} />
<RiArrowDownSLine className={`w-3 h-3 ${(currentModel && currentProvider) ? 'text-text-tertiary' : 'text-text-accent'}`} />
{
currentModel && currentModel.status !== ModelStatusEnum.active && (
<Tooltip popupContent={MODEL_STATUS_TEXT[currentModel.status][language]}>

View file

@ -1,300 +0,0 @@
/* eslint-disable multiline-ternary */
'use client'
import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import {
RiAddLine,
RiDeleteBinLine,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
import produce from 'immer'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import { ReactSortable } from 'react-sortablejs'
import cn from '@/utils/classnames'
import ConfigContext from '@/context/debug-configuration'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import Button from '@/app/components/base/button'
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
import { getInputKeys } from '@/app/components/base/block-input'
import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var'
import { getNewVar } from '@/utils/var'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
import Toast from '@/app/components/base/toast'
const MAX_QUESTION_NUM = 10
export type IOpeningStatementProps = {
value: string
readonly?: boolean
onChange?: (value: string) => void
suggestedQuestions?: string[]
onSuggestedQuestionsChange?: (value: string[]) => void
}
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
const OpeningStatement: FC<IOpeningStatementProps> = ({
value = '',
readonly,
onChange,
suggestedQuestions = [],
onSuggestedQuestionsChange = () => { },
}) => {
const { t } = useTranslation()
const {
modelConfig,
setModelConfig,
} = useContext(ConfigContext)
const promptVariables = modelConfig.configs.prompt_variables
const [notIncludeKeys, setNotIncludeKeys] = useState<string[]>([])
const hasValue = !!(value || '').trim()
const inputRef = useRef<HTMLTextAreaElement>(null)
const [isFocus, { setTrue: didSetFocus, setFalse: setBlur }] = useBoolean(false)
const setFocus = () => {
didSetFocus()
setTimeout(() => {
const input = inputRef.current
if (input) {
input.focus()
input.setSelectionRange(input.value.length, input.value.length)
}
}, 0)
}
const [tempValue, setTempValue] = useState(value)
useEffect(() => {
setTempValue(value || '')
}, [value])
const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(suggestedQuestions || [])
const notEmptyQuestions = tempSuggestedQuestions.filter(question => !!question && question.trim())
const coloredContent = (tempValue || '')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
.replace(/\n/g, '<br />')
const handleEdit = () => {
if (readonly)
return
setFocus()
}
const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
const handleCancel = () => {
setBlur()
setTempValue(value)
setTempSuggestedQuestions(suggestedQuestions)
}
const handleConfirm = () => {
if (!(tempValue || '').trim()) {
Toast.notify({
type: 'error',
message: t('common.errorMsg.fieldRequired', {
field: t('appDebug.openingStatement.title'),
}),
})
return
}
const keys = getInputKeys(tempValue)
const promptKeys = promptVariables.map(item => item.key)
let notIncludeKeys: string[] = []
if (promptKeys.length === 0) {
if (keys.length > 0)
notIncludeKeys = keys
}
else {
notIncludeKeys = keys.filter(key => !promptKeys.includes(key))
}
if (notIncludeKeys.length > 0) {
setNotIncludeKeys(notIncludeKeys)
showConfirmAddVar()
return
}
setBlur()
onChange?.(tempValue)
onSuggestedQuestionsChange(tempSuggestedQuestions)
}
const cancelAutoAddVar = () => {
onChange?.(tempValue)
hideConfirmAddVar()
setBlur()
}
const autoAddVar = () => {
const newModelConfig = produce(modelConfig, (draft) => {
draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...notIncludeKeys.map(key => getNewVar(key, 'string'))]
})
onChange?.(tempValue)
setModelConfig(newModelConfig)
hideConfirmAddVar()
setBlur()
}
const headerRight = !readonly ? (
isFocus ? (
<div className='flex items-center space-x-1'>
<Button
variant='ghost'
size='small'
onClick={handleCancel}
>
{t('common.operation.cancel')}
</Button>
<Button
onClick={handleConfirm}
variant="primary"
size='small'
>
{t('common.operation.save')}
</Button>
</div>
) : (
<OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpener') as string} onClick={handleEdit} />
)
) : null
const renderQuestions = () => {
return isFocus ? (
<div>
<div className='flex items-center py-2'>
<div className='shrink-0 flex space-x-0.5 leading-[18px] text-xs font-medium text-gray-500'>
<div className='uppercase'>{t('appDebug.openingStatement.openingQuestion')}</div>
<div>·</div>
<div>{tempSuggestedQuestions.length}/{MAX_QUESTION_NUM}</div>
</div>
<div className='ml-3 grow w-0 h-px bg-[#243, 244, 246]'></div>
</div>
<ReactSortable
className="space-y-1"
list={tempSuggestedQuestions.map((name, index) => {
return {
id: index,
name,
}
})}
setList={list => setTempSuggestedQuestions(list.map(item => item.name))}
handle='.handle'
ghostClass="opacity-50"
animation={150}
>
{tempSuggestedQuestions.map((question, index) => {
return (
<div className='group relative rounded-lg border border-gray-200 flex items-center pl-2.5 hover:border-gray-300 hover:bg-white' key={index}>
<div className='handle flex items-center justify-center w-4 h-4 cursor-grab'>
<svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M1 2C1.55228 2 2 1.55228 2 1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1C0 1.55228 0.447715 2 1 2ZM1 6C1.55228 6 2 5.55228 2 5C2 4.44772 1.55228 4 1 4C0.447715 4 0 4.44772 0 5C0 5.55228 0.447715 6 1 6ZM6 1C6 1.55228 5.55228 2 5 2C4.44772 2 4 1.55228 4 1C4 0.447715 4.44772 0 5 0C5.55228 0 6 0.447715 6 1ZM5 6C5.55228 6 6 5.55228 6 5C6 4.44772 5.55228 4 5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6ZM2 9C2 9.55229 1.55228 10 1 10C0.447715 10 0 9.55229 0 9C0 8.44771 0.447715 8 1 8C1.55228 8 2 8.44771 2 9ZM5 10C5.55228 10 6 9.55229 6 9C6 8.44771 5.55228 8 5 8C4.44772 8 4 8.44771 4 9C4 9.55229 4.44772 10 5 10Z" fill="#98A2B3" />
</svg>
</div>
<input
type="input"
value={question || ''}
onChange={(e) => {
const value = e.target.value
setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => {
if (index === i)
return value
return item
}))
}}
className={'w-full overflow-x-auto pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer rounded-lg'}
/>
<div
className='block absolute top-1/2 translate-y-[-50%] right-1.5 p-1 rounded-md cursor-pointer hover:bg-[#FEE4E2] hover:text-[#D92D20]'
onClick={() => {
setTempSuggestedQuestions(tempSuggestedQuestions.filter((_, i) => index !== i))
}}
>
<RiDeleteBinLine className='w-3.5 h-3.5' />
</div>
</div>
)
})}</ReactSortable>
{tempSuggestedQuestions.length < MAX_QUESTION_NUM && (
<div
onClick={() => { setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }}
className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400 bg-gray-100 hover:bg-gray-200'>
<RiAddLine className='w-4 h-4' />
<div className='text-gray-500 text-[13px]'>{t('appDebug.variableConfig.addOption')}</div>
</div>
)}
</div>
) : (
<div className='mt-1.5 flex flex-wrap'>
{notEmptyQuestions.map((question, index) => {
return (
<div key={index} className='mt-1 mr-1 max-w-full truncate last:mr-0 shrink-0 leading-8 items-center px-2.5 rounded-lg border border-gray-200 shadow-xs bg-white text-[13px] font-normal text-gray-900 cursor-pointer'>
{question}
</div>
)
})}
</div>
)
}
return (
<Panel
className={cn(isShowConfirmAddVar && 'h-[220px]', 'relative mt-4 !bg-gray-25')}
title={t('appDebug.openingStatement.title')}
headerIcon={
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M8.33353 1.33301C4.83572 1.33301 2.00019 4.16854 2.00019 7.66634C2.00019 8.37301 2.11619 9.05395 2.3307 9.69036C2.36843 9.80229 2.39063 9.86853 2.40507 9.91738L2.40979 9.93383L2.40729 9.93903C2.39015 9.97437 2.36469 10.0218 2.31705 10.11L1.2158 12.1484C1.14755 12.2746 1.07633 12.4064 1.02735 12.5209C0.978668 12.6348 0.899813 12.8437 0.938613 13.0914C0.984094 13.3817 1.15495 13.6373 1.40581 13.7903C1.61981 13.9208 1.843 13.9279 1.96683 13.9264C2.09141 13.925 2.24036 13.9095 2.38314 13.8947L5.81978 13.5395C5.87482 13.5338 5.9036 13.5309 5.92468 13.5292L5.92739 13.529L5.93564 13.532C5.96154 13.5413 5.99666 13.5548 6.0573 13.5781C6.76459 13.8506 7.53244 13.9997 8.33353 13.9997C11.8313 13.9997 14.6669 11.1641 14.6669 7.66634C14.6669 4.16854 11.8313 1.33301 8.33353 1.33301ZM5.9799 5.72116C6.73142 5.08698 7.73164 5.27327 8.33144 5.96584C8.93125 5.27327 9.91854 5.09365 10.683 5.72116C11.4474 6.34867 11.5403 7.41567 10.9501 8.16572C10.5845 8.6304 9.6668 9.47911 9.02142 10.0576C8.78435 10.2702 8.66582 10.3764 8.52357 10.4192C8.40154 10.456 8.26134 10.456 8.13931 10.4192C7.99706 10.3764 7.87853 10.2702 7.64147 10.0576C6.99609 9.47911 6.07839 8.6304 5.71276 8.16572C5.12259 7.41567 5.22839 6.35534 5.9799 5.72116Z" fill="#E74694" />
</svg>
}
headerRight={headerRight}
hasHeaderBottomBorder={!hasValue}
isFocus={isFocus}
>
<div className='text-gray-700 text-sm'>
{(hasValue || (!hasValue && isFocus)) ? (
<>
{isFocus
? (
<div>
<textarea
ref={inputRef}
value={tempValue}
rows={3}
onChange={e => setTempValue(e.target.value)}
className="w-full px-0 text-sm border-0 bg-transparent focus:outline-none "
placeholder={t('appDebug.openingStatement.placeholder') as string}
>
</textarea>
</div>
)
: (
<div dangerouslySetInnerHTML={{
__html: coloredContent,
}}></div>
)}
{renderQuestions()}
</>) : (
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.openingStatement.noDataPlaceHolder')}</div>
)}
{isShowConfirmAddVar && (
<ConfirmAddVar
varNameArr={notIncludeKeys}
onConfirm={autoAddVar}
onCancel={cancelAutoAddVar}
onHide={hideConfirmAddVar}
/>
)}
</div>
</Panel>
)
}
export default React.memo(OpeningStatement)

View file

@ -19,6 +19,7 @@ import {
} from '@/app/components/app/configuration/debug/hooks'
import type { ModelAndParameter } from '@/app/components/app/configuration/debug/types'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import AppPublisher from '@/app/components/app/app-publisher/features-wrapper'
import type {
@ -59,7 +60,7 @@ import {
useTextGenerationCurrentProviderAndModelAndModelList,
} from '@/app/components/header/account-setting/model-provider-page/hooks'
import { fetchCollectionList } from '@/service/tools'
import { type Collection } from '@/app/components/tools/types'
import type { Collection } from '@/app/components/tools/types'
import { useStore as useAppStore } from '@/app/components/app/store'
import {
getMultipleRetrievalConfig,
@ -71,6 +72,8 @@ import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import NewFeaturePanel from '@/app/components/base/features/new-feature-panel'
import { fetchFileUploadConfig } from '@/service/common'
import { correctProvider } from '@/utils'
import PluginDependency from '@/app/components/workflow/plugin-dependency'
type PublishConfig = {
modelConfig: ModelConfig
@ -156,7 +159,7 @@ const Configuration: FC = () => {
const setCompletionParams = (value: FormValue) => {
const params = { ...value }
// eslint-disable-next-line @typescript-eslint/no-use-before-define
// eslint-disable-next-line ts/no-use-before-define
if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) {
params.stop = getTempStop()
setTempStop([])
@ -165,7 +168,7 @@ const Configuration: FC = () => {
}
const [modelConfig, doSetModelConfig] = useState<ModelConfig>({
provider: 'openai',
provider: 'langgenius/openai/openai',
model_id: 'gpt-3.5-turbo',
mode: ModelModeType.unset,
configs: {
@ -188,7 +191,7 @@ const Configuration: FC = () => {
const isAgent = mode === 'agent-chat'
const isOpenAI = modelConfig.provider === 'openai'
const isOpenAI = modelConfig.provider === 'langgenius/openai/openai'
const [collectionList, setCollectionList] = useState<Collection[]>([])
useEffect(() => {
@ -361,7 +364,7 @@ const Configuration: FC = () => {
const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true)
const setPromptMode = async (mode: PromptMode) => {
if (mode === PromptMode.advanced) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
// eslint-disable-next-line ts/no-use-before-define
await migrateToDefaultPrompt()
setCanReturnToSimpleMode(true)
}
@ -547,8 +550,19 @@ const Configuration: FC = () => {
if (modelConfig.retriever_resource)
setCitationConfig(modelConfig.retriever_resource)
if (modelConfig.annotation_reply)
setAnnotationConfig(modelConfig.annotation_reply, true)
if (modelConfig.annotation_reply) {
let annotationConfig = modelConfig.annotation_reply
if (modelConfig.annotation_reply.enabled) {
annotationConfig = {
...modelConfig.annotation_reply,
embedding_model: {
...modelConfig.annotation_reply.embedding_model,
embedding_provider_name: correctProvider(modelConfig.annotation_reply.embedding_model.embedding_provider_name),
},
}
}
setAnnotationConfig(annotationConfig, true)
}
if (modelConfig.sensitive_word_avoidance)
setModerationConfig(modelConfig.sensitive_word_avoidance)
@ -558,7 +572,7 @@ const Configuration: FC = () => {
const config = {
modelConfig: {
provider: model.provider,
provider: correctProvider(model.provider),
model_id: model.name,
mode: model.mode,
configs: {
@ -600,7 +614,6 @@ const Configuration: FC = () => {
annotation_reply: modelConfig.annotation_reply,
external_data_tools: modelConfig.external_data_tools,
dataSets: datasets || [],
// eslint-disable-next-line multiline-ternary
agentConfig: res.mode === 'agent-chat' ? {
max_iteration: DEFAULT_AGENT_SETTING.max_iteration,
...modelConfig.agent_mode,
@ -611,8 +624,12 @@ const Configuration: FC = () => {
}).map((tool: any) => {
return {
...tool,
isDeleted: res.deleted_tools?.includes(tool.tool_name),
isDeleted: res.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name),
notAuthor: collectionList.find(c => tool.provider_id === c.id)?.is_team_authorization === false,
...(tool.provider_type === 'builtin' ? {
provider_id: correctProvider(tool.provider_name),
provider_name: correctProvider(tool.provider_name),
} : {}),
}
}),
} : DEFAULT_AGENT_SETTING,
@ -633,6 +650,12 @@ const Configuration: FC = () => {
retrieval_model: RETRIEVE_TYPE.multiWay,
...modelConfig.dataset_configs,
...retrievalConfig,
...(retrievalConfig.reranking_model ? {
reranking_model: {
...retrievalConfig.reranking_model,
reranking_provider_name: correctProvider(modelConfig.dataset_configs.reranking_model.reranking_provider_name),
},
} : {}),
})
setHasFetchedDetail(true)
})
@ -873,13 +896,13 @@ const Configuration: FC = () => {
<div className="flex flex-col h-full">
<div className='relative flex grow h-[200px] pt-14'>
{/* Header */}
<div className='absolute top-0 left-0 w-full bg-white h-14'>
<div className='absolute top-0 left-0 w-full bg-default-subtle h-14'>
<div className='flex items-center justify-between px-6 h-14'>
<div className='flex items-center'>
<div className='text-base font-semibold leading-6 text-gray-900'>{t('appDebug.orchestrate')}</div>
<div className='system-xl-semibold text-text-primary'>{t('appDebug.orchestrate')}</div>
<div className='flex items-center h-[14px] space-x-1 text-xs'>
{isAdvancedMode && (
<div className='ml-1 flex items-center h-5 px-1.5 border border-gray-100 rounded-md text-[11px] font-medium text-gray-500 uppercase'>{t('appDebug.promptMode.advanced')}</div>
<div className='ml-1 flex items-center h-5 px-1.5 border border-components-button-secondary-border rounded-md system-xs-medium-uppercase text-text-tertiary uppercase'>{t('appDebug.promptMode.advanced')}</div>
)}
</div>
</div>
@ -915,13 +938,13 @@ const Configuration: FC = () => {
debugWithMultipleModel={debugWithMultipleModel}
onDebugWithMultipleModelChange={handleDebugWithMultipleModelChange}
/>
<div className='mx-2 w-[1px] h-[14px] bg-gray-200'></div>
<Divider type='vertical' className='mx-2 h-[14px]' />
</>
)}
{isMobile && (
<Button className='!h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
<Button className='mr-2 !h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
<span className='mr-1'>{t('appDebug.operation.debugConfig')}</span>
<CodeBracketIcon className="w-4 h-4 text-gray-500" />
<CodeBracketIcon className="w-4 h-4 text-text-tertiary" />
</Button>
)}
<AppPublisher {...{
@ -992,7 +1015,7 @@ const Configuration: FC = () => {
/>
)}
{isMobile && (
<Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null} panelClassname='!bg-gray-50'>
<Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null}>
<Debug
isAPIKeySet={isAPIKeySet}
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
@ -1020,6 +1043,7 @@ const Configuration: FC = () => {
onAutoAddPromptVariable={handleAddPromptVariable}
/>
)}
<PluginDependency />
</>
</FeaturesProvider>
</ConfigContext.Provider>

View file

@ -157,7 +157,7 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
))}
{visionConfig?.enabled && (
<div className="mt-3 xl:flex justify-between">
<div className="mr-1 py-2 shrink-0 w-[120px] text-sm text-gray-900">{t('common.imageUploader.imageUpload')}</div>
<div className="mr-1 py-2 shrink-0 w-[120px] text-sm text-text-primary">{t('common.imageUploader.imageUpload')}</div>
<div className='grow'>
<TextGenerationImageUploader
settings={visionConfig}

View file

@ -1,124 +0,0 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { usePathname, useRouter } from 'next/navigation'
import ConfigParamModal from './config-param-modal'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import Tooltip from '@/app/components/base/tooltip'
import { LinkExternal02, Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import ConfigContext from '@/context/debug-configuration'
import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type'
import { fetchAnnotationConfig, updateAnnotationScore } from '@/service/annotation'
import type { AnnotationReplyConfig as AnnotationReplyConfigType } from '@/models/debug'
type Props = {
onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void
onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void
}
export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> = ({
title,
tooltip,
children,
}) => {
return (
<div>
<div className='flex items-center space-x-1'>
<div>{title}</div>
<Tooltip
popupContent={
<div className='max-w-[200px] leading-[18px] text-[13px] font-medium text-gray-800'>{tooltip}</div>
}
/>
</div>
<div>{children}</div>
</div>
)
}
const AnnotationReplyConfig: FC<Props> = ({
onEmbeddingChange,
onScoreChange,
}) => {
const { t } = useTranslation()
const router = useRouter()
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const {
annotationConfig,
} = useContext(ConfigContext)
const [isShowEdit, setIsShowEdit] = React.useState(false)
return (
<>
<Panel
className="mt-4"
headerIcon={
<MessageFast className='w-4 h-4 text-[#444CE7]' />
}
title={t('appDebug.feature.annotation.title')}
headerRight={
<div className='flex items-center'>
<div
className='flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200'
onClick={() => { setIsShowEdit(true) }}
>
<Settings04 className="w-[14px] h-[14px]" />
<div className='text-xs font-medium'>
{t('common.operation.params')}
</div>
</div>
<div
className='ml-1 flex items-center h-7 px-3 space-x-1 leading-[18px] text-xs font-medium text-gray-700 rounded-md cursor-pointer hover:bg-gray-200'
onClick={() => {
router.push(`/app/${appId}/annotations`)
}}>
<div>{t('appDebug.feature.annotation.cacheManagement')}</div>
<LinkExternal02 className='w-3.5 h-3.5' />
</div>
</div>
}
noBodySpacing
/>
{isShowEdit && (
<ConfigParamModal
appId={appId}
isShow
onHide={() => {
setIsShowEdit(false)
}}
onSave={async (embeddingModel, score) => {
const annotationConfig = await fetchAnnotationConfig(appId) as AnnotationReplyConfigType
let isEmbeddingModelChanged = false
if (
embeddingModel.embedding_model_name !== annotationConfig.embedding_model.embedding_model_name
|| embeddingModel.embedding_provider_name !== annotationConfig.embedding_model.embedding_provider_name
) {
await onEmbeddingChange(embeddingModel)
isEmbeddingModelChanged = true
}
if (score !== annotationConfig.score_threshold) {
await updateAnnotationScore(appId, annotationConfig.id, score)
if (isEmbeddingModelChanged)
onScoreChange(score, embeddingModel)
else
onScoreChange(score)
}
setIsShowEdit(false)
}}
annotationConfig={annotationConfig}
/>
)}
</>
)
}
export default React.memo(AnnotationReplyConfig)

View file

@ -1,45 +0,0 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import GroupName from '../base/group-name'
import Moderation from './moderation'
import Annotation from './annotation/config-param'
import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type'
export type ToolboxProps = {
showModerationSettings: boolean
showAnnotation: boolean
onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void
onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void
}
const Toolbox: FC<ToolboxProps> = ({
showModerationSettings,
showAnnotation,
onEmbeddingChange,
onScoreChange,
}) => {
const { t } = useTranslation()
return (
<div className='mt-7'>
<GroupName name={t('appDebug.feature.toolbox.title')} />
{
showModerationSettings && (
<Moderation />
)
}
{
showAnnotation && (
<Annotation
onEmbeddingChange={onEmbeddingChange}
onScoreChange={onScoreChange}
/>
)
}
</div>
)
}
export default React.memo(Toolbox)

View file

@ -21,13 +21,13 @@ import { useToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
const systemTypes = ['api']
type ExternalDataToolModalProps = {
interface ExternalDataToolModalProps {
data: ExternalDataTool
onCancel: () => void
onSave: (externalDataTool: ExternalDataTool) => void
onValidateBeforeSave?: (externalDataTool: ExternalDataTool) => boolean
}
type Provider = {
interface Provider {
key: string
name: string
form_schema?: CodeBasedExtensionItem['form_schema']

View file

@ -27,6 +27,7 @@ import { getRedirection } from '@/utils/app-redirection'
import Input from '@/app/components/base/input'
import type { AppMode } from '@/types/app'
import { DSLImportMode } from '@/models/app'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
type AppsProps = {
onSuccess?: () => void
@ -119,6 +120,7 @@ const Apps = ({
const [currApp, setCurrApp] = React.useState<App | null>(null)
const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
const { handleCheckPluginDependencies } = usePluginDependencies()
const onCreate: CreateAppModalProps['onConfirm'] = async ({
name,
icon_type,
@ -146,6 +148,8 @@ const Apps = ({
})
if (onSuccess)
onSuccess()
if (app.app_id)
await handleCheckPluginDependencies(app.app_id)
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
getRedirection(isCurrentWorkspaceEditor, { id: app.app_id }, push)
}

View file

@ -25,6 +25,7 @@ import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { getRedirection } from '@/utils/app-redirection'
import cn from '@/utils/classnames'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
type CreateFromDSLModalProps = {
show: boolean
@ -50,6 +51,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
const [showErrorModal, setShowErrorModal] = useState(false)
const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>()
const [importId, setImportId] = useState<string>()
const { handleCheckPluginDependencies } = usePluginDependencies()
const readFile = (file: File) => {
const reader = new FileReader()
@ -114,6 +116,8 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('app.newApp.appCreateDSLWarning'),
})
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
if (app_id)
await handleCheckPluginDependencies(app_id)
getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push)
}
else if (status === DSLImportStatus.PENDING) {
@ -132,6 +136,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
}
// eslint-disable-next-line unused-imports/no-unused-vars
catch (e) {
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
@ -158,6 +163,8 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
type: 'success',
message: t('app.newApp.appCreated'),
})
if (app_id)
await handleCheckPluginDependencies(app_id)
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push)
}
@ -165,6 +172,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
}
// eslint-disable-next-line unused-imports/no-unused-vars
catch (e) {
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
@ -268,7 +276,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
>
<div className='flex pb-4 flex-col items-start gap-2 self-stretch'>
<div className='text-text-primary title-2xl-semi-bold'>{t('app.newApp.appCreateDSLErrorTitle')}</div>
<div className='flex flex-grow flex-col text-text-secondary system-md-regular'>
<div className='flex grow flex-col text-text-secondary system-md-regular'>
<div>{t('app.newApp.appCreateDSLErrorPart1')}</div>
<div>{t('app.newApp.appCreateDSLErrorPart2')}</div>
<br />

View file

@ -13,7 +13,7 @@ import { useProviderContext } from '@/context/provider-context'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import type { AppIconType } from '@/types/app'
export type DuplicateAppModalProps = {
export interface DuplicateAppModalProps {
appName: string
icon_type: AppIconType | null
icon: string

View file

@ -79,6 +79,9 @@ const HandThumbIconWithCount: FC<{ count: number; iconType: 'up' | 'down' }> = (
}
const statusTdRender = (statusCount: StatusCount) => {
if (!statusCount)
return null
if (statusCount.partial_success + statusCount.failed === 0) {
return (
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>

View file

@ -27,8 +27,8 @@ const APIKeyInfoPanel: FC = () => {
return null
return (
<div className={cn('bg-[#EFF4FF] border-[#D1E0FF]', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
<div className={cn('text-[24px] text-gray-800 font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
<div className={cn('bg-components-panel-bg border-components-panel-border', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
<div className={cn('text-[24px] text-text-primary font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
{isCloud && <em-emoji id={'😀'} />}
{isCloud
? (
@ -42,11 +42,11 @@ const APIKeyInfoPanel: FC = () => {
)}
</div>
{isCloud && (
<div className='mt-1 text-sm text-gray-600 font-normal'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
<div className='mt-1 text-sm text-text-tertiary font-normal'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
)}
<Button
variant='primary'
className='space-x-2'
className='mt-2 space-x-2'
onClick={() => setShowAccountSettingModal({ payload: 'provider' })}
>
<div className='text-sm font-medium'>{t('appOverview.apiKeyInfo.setAPIBtn')}</div>
@ -65,7 +65,7 @@ const APIKeyInfoPanel: FC = () => {
<div
onClick={() => setIsShow(false)}
className='absolute right-4 top-4 flex items-center justify-center w-8 h-8 cursor-pointer '>
<RiCloseLine className='w-4 h-4 text-gray-500' />
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
</div>
</div>
)

View file

@ -1,29 +0,0 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import s from './style.module.css'
import cn from '@/utils/classnames'
export type IProgressProps = {
className?: string
value: number // percent
}
const Progress: FC<IProgressProps> = ({
className,
value,
}) => {
const exhausted = value === 100
return (
<div className={cn(className, 'relative grow h-2 flex bg-gray-200 rounded-md overflow-hidden')}>
<div
className={cn(s.bar, exhausted && s['bar-error'], 'absolute top-0 left-0 right-0 bottom-0')}
style={{ width: `${value}%` }}
/>
{Array(10).fill(0).map((i, k) => (
<div key={k} className={s['bar-item']} />
))}
</div>
)
}
export default React.memo(Progress)

View file

@ -1,16 +0,0 @@
.bar {
background: linear-gradient(90deg, rgba(41, 112, 255, 0.9) 0%, rgba(21, 94, 239, 0.9) 100%);
}
.bar-error {
background: linear-gradient(90deg, rgba(240, 68, 56, 0.72) 0%, rgba(217, 45, 32, 0.9) 100%);
}
.bar-item {
width: 10%;
border-right: 1px solid rgba(255, 255, 255, 0.5);
}
.bar-item:last-of-type {
border-right: 0;
}

View file

@ -215,8 +215,8 @@ const Chart: React.FC<IChartProps> = ({
return `<div style='color:#6B7280;font-size:12px'>${params.name}</div>
<div style='font-size:14px;color:#1F2A37'>${valueFormatter((params.data as any)[yField])}
${!CHART_TYPE_CONFIG[chartType].showTokens
? ''
: `<span style='font-size:12px'>
? ''
: `<span style='font-size:12px'>
<span style='margin-left:4px;color:#6B7280'>(</span>
<span style='color:#FF8A4C'>~$${get(params.data, 'total_price', 0)}</span>
<span style='color:#6B7280'>)</span>
@ -230,7 +230,7 @@ const Chart: React.FC<IChartProps> = ({
const sumData = isAvg ? (sum(yData) / yData.length) : sum(yData)
return (
<div className={`flex flex-col w-full px-6 py-4 border-[0.5px] rounded-lg border-gray-200 shadow-xs ${className ?? ''}`}>
<div className={`flex flex-col w-full px-6 py-4 rounded-xl bg-components-chart-bg shadow-xs ${className ?? ''}`}>
<div className='mb-3'>
<Basic name={title} type={timePeriod} hoverTip={explanation} />
</div>
@ -241,11 +241,11 @@ const Chart: React.FC<IChartProps> = ({
type={!CHART_TYPE_CONFIG[chartType].showTokens
? ''
: <span>{t('appOverview.analysis.tokenUsage.consumed')} Tokens<span className='text-sm'>
<span className='ml-1 text-gray-500'>(</span>
<span className='text-orange-400'>~{sum(statistics.map(item => parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })}</span>
<span className='text-gray-500'>)</span>
<span className='ml-1 text-text-tertiary'>(</span>
<span className='text-orange-400'>~{sum(statistics.map(item => Number.parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })}</span>
<span className='text-text-tertiary'>)</span>
</span></span>}
textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-gray-300' : ''}` }} />
textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-text-quaternary' : ''}` }} />
</div>
<ReactECharts option={options} style={{ height: 160 }} />
</div>

View file

@ -21,7 +21,7 @@ type IShareLinkProps = {
}
const StepNum: FC<{ children: React.ReactNode }> = ({ children }) =>
<div className='h-7 w-7 flex justify-center items-center flex-shrink-0 mr-3 text-primary-600 bg-primary-50 rounded-2xl'>
<div className='h-7 w-7 flex justify-center items-center shrink-0 mr-3 text-text-accent bg-util-colors-blue-blue-50 rounded-2xl'>
{children}
</div>
@ -54,27 +54,27 @@ const CustomizeModal: FC<IShareLinkProps> = ({
className='!max-w-2xl w-[640px]'
closable={true}
>
<div className='w-full mt-4 px-6 py-5 border-gray-200 rounded-lg border-[0.5px]'>
<Tag bordered={true} hideBg={true} className='text-primary-600 border-primary-600 uppercase'>{t(`${prefixCustomize}.way`)} 1</Tag>
<p className='my-2 text-base font-medium text-gray-800'>{t(`${prefixCustomize}.way1.name`)}</p>
<div className='w-full mt-4 px-6 py-5 border-components-panel-border rounded-lg border-[0.5px]'>
<Tag bordered={true} hideBg={true} className='text-text-accent-secondary border-text-accent-secondary uppercase'>{t(`${prefixCustomize}.way`)} 1</Tag>
<p className='my-2 system-sm-medium text-text-secondary'>{t(`${prefixCustomize}.way1.name`)}</p>
<div className='flex py-4'>
<StepNum>1</StepNum>
<div className='flex flex-col'>
<div className='text-gray-900'>{t(`${prefixCustomize}.way1.step1`)}</div>
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step1Tip`)}</div>
<div className='text-text-primary'>{t(`${prefixCustomize}.way1.step1`)}</div>
<div className='text-text-tertiary text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step1Tip`)}</div>
<a href={`https://github.com/langgenius/${isChatApp ? 'webapp-conversation' : 'webapp-text-generator'}`} target='_blank' rel='noopener noreferrer'>
<Button><GithubIcon className='text-gray-800 mr-2' />{t(`${prefixCustomize}.way1.step1Operation`)}</Button>
<Button><GithubIcon className='text-text-secondary mr-2' />{t(`${prefixCustomize}.way1.step1Operation`)}</Button>
</a>
</div>
</div>
<div className='flex pt-4'>
<StepNum>2</StepNum>
<div className='flex flex-col'>
<div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step2Tip`)}</div>
<div className='text-text-primary'>{t(`${prefixCustomize}.way1.step3`)}</div>
<div className='text-text-tertiary text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step2Tip`)}</div>
<a href="https://vercel.com/docs/concepts/deployments/git/vercel-for-github" target='_blank' rel='noopener noreferrer'>
<Button>
<div className='mr-1.5 border-solid border-t-0 border-r-[7px] border-l-[7px] border-b-[12px] border-r-transparent border-b-black border-l-transparent border-t-transparent'></div>
<div className='mr-1.5 border-solid border-t-0 border-r-[7px] border-l-[7px] border-b-[12px] border-r-transparent border-text-primary border-l-transparent border-t-transparent'></div>
<span>{t(`${prefixCustomize}.way1.step2Operation`)}</span>
</Button>
</a>
@ -83,9 +83,9 @@ const CustomizeModal: FC<IShareLinkProps> = ({
<div className='flex py-4'>
<StepNum>3</StepNum>
<div className='flex flex-col w-full overflow-hidden'>
<div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
<pre className='overflow-x-scroll box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
<div className='text-text-primary'>{t(`${prefixCustomize}.way1.step3`)}</div>
<div className='text-text-tertiary text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
<pre className='overflow-x-scroll box-border py-3 px-4 bg-background-section text-xs font-medium rounded-lg select-text text-text-secondary border-[0.5px] border-components-panel-border'>
NEXT_PUBLIC_APP_ID={`'${appId}'`} <br />
NEXT_PUBLIC_APP_KEY={'\'<Web API Key From Dify>\''} <br />
NEXT_PUBLIC_API_URL={`'${api_base_url}'`}
@ -94,9 +94,9 @@ const CustomizeModal: FC<IShareLinkProps> = ({
</div>
</div>
<div className='w-full mt-4 px-6 py-5 border-gray-200 rounded-lg border-[0.5px]'>
<Tag bordered={true} hideBg={true} className='text-primary-600 border-primary-600 uppercase'>{t(`${prefixCustomize}.way`)} 2</Tag>
<p className='mt-2 text-base font-medium text-gray-800'>{t(`${prefixCustomize}.way2.name`)}</p>
<div className='w-full mt-4 px-6 py-5 border-components-panel-border rounded-lg border-[0.5px]'>
<Tag bordered={true} hideBg={true} className='text-text-accent-secondary border-text-accent-secondary uppercase'>{t(`${prefixCustomize}.way`)} 2</Tag>
<p className='my-2 system-sm-medium text-text-secondary'>{t(`${prefixCustomize}.way2.name`)}</p>
<Button
className='mt-2'
onClick={() =>
@ -109,8 +109,8 @@ const CustomizeModal: FC<IShareLinkProps> = ({
)
}
>
<span className='text-sm text-gray-800'>{t(`${prefixCustomize}.way2.operation`)}</span>
<ArrowTopRightOnSquareIcon className='w-4 h-4 ml-1 text-gray-800 shrink-0' />
<span className='text-sm text-text-secondary'>{t(`${prefixCustomize}.way2.operation`)}</span>
<ArrowTopRightOnSquareIcon className='w-4 h-4 ml-1 text-text-secondary shrink-0' />
</Button>
</div>
</Modal>

View file

@ -1,15 +1,19 @@
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiClipboardFill,
RiClipboardLine,
} from '@remixicon/react'
import copy from 'copy-to-clipboard'
import style from './style.module.css'
import cn from '@/utils/classnames'
import Modal from '@/app/components/base/modal'
import copyStyle from '@/app/components/base/copy-btn/style.module.css'
import Tooltip from '@/app/components/base/tooltip'
import { useAppContext } from '@/context/app-context'
import { IS_CE_EDITION } from '@/config'
import type { SiteInfo } from '@/models/share'
import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context'
import ActionButton from '@/app/components/base/action-button'
import cn from '@/utils/classnames'
type Props = {
siteInfo?: SiteInfo
@ -35,12 +39,12 @@ const OPTION_MAP = {
`<script>
window.difyChatbotConfig = {
token: '${token}'${isTestEnv
? `,
? `,
isDev: true`
: ''}${IS_CE_EDITION
? `,
: ''}${IS_CE_EDITION
? `,
baseUrl: '${url}'`
: ''}
: ''}
}
</script>
<script
@ -119,7 +123,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
wrapperClassName={className}
closable={true}
>
<div className="mb-4 mt-8 text-gray-900 text-[14px] font-medium leading-tight">
<div className="mb-4 mt-8 text-text-primary system-sm-medium">
{t(`${prefixEmbedded}.explanation`)}
</div>
<div className="flex flex-wrap items-center justify-between gap-y-2">
@ -143,30 +147,37 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
{option === 'chromePlugin' && (
<div className="w-full mt-6">
<div className={cn('gap-2 py-3 justify-center items-center inline-flex w-full rounded-lg',
'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm flex-shrink-0')}>
'bg-primary-600 hover:bg-primary-600/75 cursor-pointer text-white hover:shadow-sm flex-shrink-0')}>
<div className={`w-4 h-4 relative ${style.pluginInstallIcon}`}></div>
<div className="text-white text-sm font-medium font-['Inter'] leading-tight" onClick={navigateToChromeUrl}>{t(`${prefixEmbedded}.chromePlugin`)}</div>
</div>
</div>
)}
<div className={cn('w-full bg-gray-100 rounded-lg flex-col justify-start items-start inline-flex',
<div className={cn('w-full bg-background-section border-[0.5px] border-components-panel-border rounded-lg flex-col justify-start items-start inline-flex',
'mt-6')}>
<div className="inline-flex items-center self-stretch justify-start gap-2 py-1 pl-3 pr-1 border border-black rounded-tl-lg rounded-tr-lg bg-gray-50 border-opacity-5">
<div className="grow shrink basis-0 text-slate-700 text-[13px] font-medium leading-none">
<div className="inline-flex items-center self-stretch justify-start gap-2 py-1 pl-3 pr-1 rounded-t-lg bg-background-section-burn">
<div className="grow shrink-0 text-text-secondary system-sm-medium">
{t(`${prefixEmbedded}.${option}`)}
</div>
<div className="flex items-center justify-center gap-1 p-2 rounded-lg">
<Tooltip
popupContent={(isCopied[option] ? t(`${prefixEmbedded}.copied`) : t(`${prefixEmbedded}.copy`)) || ''}
>
<div className="w-8 h-8 rounded-lg cursor-pointer hover:bg-gray-100">
<div onClick={onClickCopy} className={`w-full h-full ${copyStyle.copyIcon} ${isCopied[option] ? copyStyle.copied : ''}`}></div>
<Tooltip
popupContent={
(isCopied[option]
? t(`${prefixEmbedded}.copied`)
: t(`${prefixEmbedded}.copy`)) || ''
}
>
<ActionButton>
<div
onClick={onClickCopy}
>
{isCopied[option] && <RiClipboardFill className='w-4 h-4' />}
{!isCopied[option] && <RiClipboardLine className='w-4 h-4' />}
</div>
</Tooltip>
</div>
</ActionButton>
</Tooltip>
</div>
<div className="flex items-start justify-start w-full gap-2 p-3 overflow-x-auto">
<div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
<div className="grow shrink basis-0 text-text-secondary text-[13px] leading-tight font-mono">
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)}</pre>
</div>
</div>

View file

@ -355,7 +355,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
<div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.entry`)}</div>
<p className={cn('pb-0.5 text-text-tertiary body-xs-regular')}>{t(`${prefixSettings}.more.copyRightPlaceholder`)} & {t(`${prefixSettings}.more.privacyPolicyPlaceholder`)}</p>
</div>
<RiArrowRightSLine className='shrink-0 ml-1 w-4 h-4 text-text-secondary'/>
<RiArrowRightSLine className='shrink-0 ml-1 w-4 h-4 text-text-secondary' />
</div>
)}
{/* more settings */}

View file

@ -2,7 +2,7 @@ import { create } from 'zustand'
import type { App, AppSSO } from '@/types/app'
import type { IChatItem } from '@/app/components/base/chat/chat/type'
type State = {
interface State {
appDetail?: App & Partial<AppSSO>
appSidebarExpand: string
currentLogItem?: IChatItem
@ -13,7 +13,7 @@ type State = {
showAppConfigureFeaturesModal: boolean
}
type Action = {
interface Action {
setAppDetail: (appDetail?: App & Partial<AppSSO>) => void
setAppSiderbarExpand: (state: string) => void
setCurrentLogItem: (item?: IChatItem) => void

View file

@ -25,7 +25,7 @@ import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/aler
import AppIcon from '@/app/components/base/app-icon'
import { useStore as useAppStore } from '@/app/components/app/store'
type SwitchAppModalProps = {
interface SwitchAppModalProps {
show: boolean
appDetail: App
onSuccess?: () => void

View file

@ -33,7 +33,7 @@ import { useChatContext } from '@/app/components/base/chat/chat/context'
const MAX_DEPTH = 3
export type IGenerationItemProps = {
export interface IGenerationItemProps {
isWorkflow?: boolean
workflowProcessData?: WorkflowProcess
className?: string

View file

@ -9,7 +9,7 @@ import {
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication'
import { type AppMode } from '@/types/app'
import type { AppMode } from '@/types/app'
export type AppSelectorProps = {
value: Array<AppMode>
onChange: (value: AppSelectorProps['value']) => void

View file

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import Run from '@/app/components/workflow/run'
type ILogDetail = {
interface ILogDetail {
runID: string
onClose: () => void
}

View file

@ -6,7 +6,7 @@ import type { QueryParam } from './index'
import Chip from '@/app/components/base/chip'
import Input from '@/app/components/base/input'
type IFilterProps = {
interface IFilterProps {
queryParams: QueryParam
setQueryParams: (v: QueryParam) => void
}

View file

@ -2,9 +2,7 @@
@layer components {
.action-btn {
@apply inline-flex justify-center items-center cursor-pointer text-text-tertiary
hover:text-text-secondary
hover:bg-state-base-hover
@apply inline-flex justify-center items-center cursor-pointer text-text-tertiary hover:text-text-secondary hover:bg-state-base-hover
}
.action-btn-disabled {
@ -29,21 +27,15 @@
}
.action-btn.action-btn-active {
@apply
text-text-accent
bg-state-accent-active
hover:bg-state-accent-active-alt
@apply text-text-accent bg-state-accent-active hover:bg-state-accent-active-alt
}
.action-btn.action-btn-disabled {
@apply
text-text-disabled
@apply text-text-disabled
}
.action-btn.action-btn-destructive {
@apply
text-text-destructive
bg-state-destructive-hover
@apply text-text-destructive bg-state-destructive-hover
}
}

View file

@ -28,7 +28,7 @@ const actionButtonVariants = cva(
)
export type ActionButtonProps = {
size?: 'xs' | 'm' | 'l' | 'xl'
size?: 'xs' | 's' | 'm' | 'l' | 'xl'
state?: ActionButtonState
styleCss?: CSSProperties
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof actionButtonVariants>

View file

@ -81,26 +81,26 @@ const AgentLogDetail: FC<AgentLogDetailProps> = ({
return (
<div className='grow relative flex flex-col'>
{/* tab */}
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-divider-regular'>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700',
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-text-tertiary cursor-pointer',
currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-text-secondary',
)}
onClick={() => switchTab('DETAIL')}
>{t('runLog.detail')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700',
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-text-tertiary cursor-pointer',
currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-text-secondary',
)}
onClick={() => switchTab('TRACING')}
>{t('runLog.tracing')}</div>
</div>
{/* panel detail */}
<div className={cn('grow bg-white h-0 overflow-y-auto rounded-b-2xl', currentTab !== 'DETAIL' && '!bg-gray-50')}>
<div className={cn('grow bg-components-panel-bg h-0 overflow-y-auto rounded-b-2xl', currentTab !== 'DETAIL' && '!bg-background-section')}>
{loading && (
<div className='flex h-full items-center justify-center bg-white'>
<div className='flex h-full items-center justify-center bg-components-panel-bg'>
<Loading />
</div>
)}

View file

@ -35,7 +35,7 @@ const AgentLogModal: FC<AgentLogModalProps> = ({
return (
<div
className={cn('relative flex flex-col py-3 bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10')}
className={cn('relative flex flex-col py-3 bg-components-panel-bg border-[0.5px] border-components-panel-border rounded-xl shadow-xl z-10')}
style={{
width: 480,
position: 'fixed',
@ -45,9 +45,9 @@ const AgentLogModal: FC<AgentLogModalProps> = ({
}}
ref={ref}
>
<h1 className='shrink-0 px-4 py-1 text-md font-semibold text-gray-900'>{t('appLog.runDetail.workflowTitle')}</h1>
<h1 className='shrink-0 px-4 py-1 text-md font-semibold text-text-primary'>{t('appLog.runDetail.workflowTitle')}</h1>
<span className='absolute right-3 top-4 p-1 cursor-pointer z-20' onClick={onCancel}>
<RiCloseLine className='w-4 h-4 text-gray-500' />
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
</span>
<AgentLogDetail
conversationID={currentLogItem.conversationId}

View file

@ -2,8 +2,9 @@
import { useTranslation } from 'react-i18next'
import type { FC } from 'react'
import ToolCall from './tool-call'
import cn from '@/utils/classnames'
import Divider from '@/app/components/base/divider'
import type { AgentIteration } from '@/models/log'
import cn from '@/utils/classnames'
type Props = {
isFinal: boolean
@ -18,12 +19,12 @@ const Iteration: FC<Props> = ({ iterationInfo, isFinal, index }) => {
<div className={cn('px-4 py-2')}>
<div className='flex items-center'>
{isFinal && (
<div className='shrink-0 mr-3 text-gray-500 text-xs leading-[18px] font-semibold'>{t('appLog.agentLogDetail.finalProcessing')}</div>
<div className='shrink-0 mr-3 text-text-tertiary text-xs leading-[18px] font-semibold'>{t('appLog.agentLogDetail.finalProcessing')}</div>
)}
{!isFinal && (
<div className='shrink-0 mr-3 text-gray-500 text-xs leading-[18px] font-semibold'>{`${t('appLog.agentLogDetail.iteration').toUpperCase()} ${index}`}</div>
<div className='shrink-0 mr-3 text-text-tertiary text-xs leading-[18px] font-semibold'>{`${t('appLog.agentLogDetail.iteration').toUpperCase()} ${index}`}</div>
)}
<div className='grow h-[1px] bg-gradient-to-r from-[#f3f4f6] to-gray-50'></div>
<Divider bgStyle='gradient' className='grow h-[1px] mx-0'/>
</div>
<ToolCall
isLLM

View file

@ -36,7 +36,7 @@ const ResultPanel: FC<ResultPanelProps> = ({
const { formatTime } = useTimestamp()
return (
<div className='bg-white py-2'>
<div className='bg-components-panel-bg py-2'>
<div className='px-4 py-2'>
<StatusPanel
status='succeeded'
@ -62,57 +62,57 @@ const ResultPanel: FC<ResultPanelProps> = ({
/>
</div>
<div className='px-4 py-2'>
<div className='h-[0.5px] bg-black opacity-5' />
<div className='h-[0.5px] bg-divider-regular opacity-5' />
</div>
<div className='px-4 py-2'>
<div className='relative'>
<div className='h-6 leading-6 text-gray-500 text-xs font-medium'>{t('runLog.meta.title')}</div>
<div className='h-6 leading-6 text-text-tertiary text-xs font-medium'>{t('runLog.meta.title')}</div>
<div className='py-1'>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.status')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('runLog.meta.status')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>SUCCESS</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.executor')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('runLog.meta.executor')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{created_by || 'N/A'}</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.startTime')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('runLog.meta.startTime')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{formatTime(Date.parse(created_at) / 1000, t('appLog.dateTimeFormat') as string)}</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.time')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('runLog.meta.time')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{`${elapsed_time?.toFixed(3)}s`}</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.tokens')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('runLog.meta.tokens')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{`${total_tokens || 0} Tokens`}</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.agentMode')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.agentMode')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{agentMode === 'function_call' ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.toolUsed')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.toolUsed')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{tools?.length ? tools?.join(', ') : 'Null'}</span>
</div>
</div>
<div className='flex'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.iterations')}</div>
<div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
<div className='shrink-0 w-[104px] px-2 py-[5px] text-text-tertiary text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.iterations')}</div>
<div className='grow px-2 py-[5px] text-text-primary text-xs leading-[18px]'>
<span>{iterations}</span>
</div>
</div>

View file

@ -33,7 +33,7 @@ const ToolCallItem: FC<Props> = ({ toolCall, isLLM = false, isFinal, tokens, obs
if (time < 1)
return `${(time * 1000).toFixed(3)} ms`
if (time > 60)
return `${parseInt(Math.round(time / 60).toString())} m ${(time % 60).toFixed(3)} s`
return `${Number.parseInt(Math.round(time / 60).toString())} m ${(time % 60).toFixed(3)} s`
return `${time.toFixed(3)} s`
}
@ -41,14 +41,14 @@ const ToolCallItem: FC<Props> = ({ toolCall, isLLM = false, isFinal, tokens, obs
if (tokens < 1000)
return tokens
if (tokens >= 1000 && tokens < 1000000)
return `${parseFloat((tokens / 1000).toFixed(3))}K`
return `${Number.parseFloat((tokens / 1000).toFixed(3))}K`
if (tokens >= 1000000)
return `${parseFloat((tokens / 1000000).toFixed(3))}M`
return `${Number.parseFloat((tokens / 1000000).toFixed(3))}M`
}
return (
<div className={cn('py-1')}>
<div className={cn('group transition-all bg-white border border-gray-100 rounded-2xl shadow-xs hover:shadow-md')}>
<div className={cn('group transition-all bg-background-default border border-components-panel-border rounded-2xl shadow-xs hover:shadow-md')}>
<div
className={cn(
'flex items-center py-3 pl-[6px] pr-3 cursor-pointer',
@ -58,15 +58,15 @@ const ToolCallItem: FC<Props> = ({ toolCall, isLLM = false, isFinal, tokens, obs
>
<ChevronRight
className={cn(
'shrink-0 w-3 h-3 mr-1 text-gray-400 transition-all group-hover:text-gray-500',
'shrink-0 w-3 h-3 mr-1 text-text-quaternary transition-all group-hover:text-text-tertiary',
!collapseState && 'rotate-90',
)}
/>
<BlockIcon className={cn('shrink-0 mr-2')} type={isLLM ? BlockEnum.LLM : BlockEnum.Tool} toolIcon={toolCall.tool_icon} />
<div className={cn(
'grow text-gray-700 text-[13px] leading-[16px] font-semibold truncate',
'grow text-text-secondary text-[13px] leading-[16px] font-semibold truncate',
)} title={toolName}>{toolName}</div>
<div className='shrink-0 text-gray-500 text-xs leading-[18px]'>
<div className='shrink-0 text-text-tertiary text-xs leading-[18px]'>
{toolCall.time_cost && (
<span>{getTime(toolCall.time_cost || 0)}</span>
)}

View file

@ -9,7 +9,7 @@ type TracingPanelProps = {
const TracingPanel: FC<TracingPanelProps> = ({ list }) => {
return (
<div className='bg-gray-50'>
<div className='bg-background-section'>
{list.map((iteration, index) => (
<Iteration
key={index}

View file

@ -0,0 +1,23 @@
.appIcon {
@apply flex items-center justify-center relative w-9 h-9 text-lg rounded-lg grow-0 shrink-0;
}
.appIcon.large {
@apply w-10 h-10;
}
.appIcon.small {
@apply w-8 h-8;
}
.appIcon.tiny {
@apply w-6 h-6 text-base;
}
.appIcon.xs {
@apply w-5 h-5 text-base;
}
.appIcon.rounded {
@apply rounded-full;
}

View file

@ -1,6 +1,6 @@
import AudioPlayer from '@/app/components/base/audio-btn/audio'
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
// eslint-disable-next-line ts/consistent-type-definitions
interface AudioPlayerManager {
instance: AudioPlayerManager
}
@ -12,6 +12,7 @@ export class AudioPlayerManager {
private audioPlayers: AudioPlayer | null = null
private msgId: string | undefined
// eslint-disable-next-line
private constructor() {
}

Some files were not shown because too many files have changed in this diff Show more