Skip to content

Commit 58f8193

Browse files
authored
Feat/integrate llm front (#150)
* fix: fix ScrolledContainer component * fix: fix icon * fix: fix ConfirmationModal component * feat: add llm api functions * feat: add LLMToken component * refactor: rewrite Settings page * feat: add LlmContext * feat: add useDebouncedCallback hook * feat: add Llms tab to Settings page * fix * fix: fix EditPen icon * fix: fix llmContext and api * fix: update LLMToken * feat: add LLMConfig * feat: add PromptRedactor * fix: small fixes * fix-test * test * fix: update functionality according to the new API (add IDs to tokens) * fix: update useDebounce hook * add test * feat: Provide id for LLMs tokens & configs * fix: Patch token value correctly * feat: Add default config model endpoint * fix: fix llm settings page * style: add scrollbar styles * hideButtons * feat: Add GET `llms/default/` & fix PATCH tokens * fix: Append tokens to the llms file properly * fix * fix: update types * fix: fix utils * fix: update routing * feat: add helper hook * feat: update llm context * feat: update response modal * feat: update condition modal * feat: add LLM-related API functions * fix: fix llms tab * chore: fix linter error * fix: update code to pass e2e tests * test: add unit tests for multiple components * stash * fix: bug with keyboard events (copy, paste, undo, redo) * fix: restore ability to change condition type * fix: Correct condition&response conversion * refactor(llm config): renamed field config_name to name * test: fix E2E tests * fix: cypress config * test: fix llm tests * fix: correct config name field * fix: Lock poetry * style up * fix: buttons bug
1 parent 3d9e7d8 commit 58f8193

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3578
-570
lines changed

backend/chatsky_ui/api/api_v1/endpoints/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ async def patch_llm_model(
289289
llms_conf["config_models"].update(
290290
{
291291
config_id: {
292-
"name": new_config_name if new_config_name else config_models[config_id]["config_name"],
292+
"name": new_config_name if new_config_name else config_models[config_id]["name"],
293293
"model_name": model_name if model_name else config_models[config_id]["model_name"],
294294
"token_id": llm_token_id if llm_token_id else config_models[config_id]["token_id"],
295295
"system_prompt": system_prompt if system_prompt else config_models[config_id]["system_prompt"],

backend/chatsky_ui/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def init(
282282
"https://github.com/deeppavlov/chatsky-ui-template.git",
283283
no_input=no_input,
284284
overwrite_if_exists=overwrite_if_exists,
285+
checkout="feat/llm2",
285286
)
286287
finally:
287288
os.chdir(original_dir)

backend/chatsky_ui/services/json_converter/logic_component_converter/buttons_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def determine_button_type(self, buttons_list: list) -> Optional[str]:
3030
3131
Raises: `KeyError`, if `buttons_dict` is a dictionary of the wrong structure.
3232
"""
33-
if buttons_list[0][0].get("type", None) == "exactMatch":
33+
if buttons_list and buttons_list[0][0].get("type", None) == "exactMatch":
3434
return "reply"
3535
else:
3636
return "inline"

frontend/cypress.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import { defineConfig } from 'cypress'
22

33
export default defineConfig({
44
e2e: {
5+
retries: {
6+
runMode: 2,
7+
openMode: 0,
8+
},
59
setupNodeEvents() {
610
// implement node event listeners here
711
},

frontend/cypress/e2e/spec.cy.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ describe('Flow creation and editing process', () => {
184184
cy.get('[data-testid=condition-modal]')
185185
.find('[data-testid=tab-slot]')
186186
.click()
187+
cy.wait(600)
187188
cy.get('input[data-testid=condition-name]')
188189
.clear()
189190
.type('Test_slots_condition')
@@ -201,6 +202,7 @@ describe('Flow creation and editing process', () => {
201202
cy.get('[data-testid=condition-modal]')
202203
.find('[data-testid=tab-basic]')
203204
.click()
205+
cy.wait(600)
204206
cy.get('input[data-testid=condition-name]')
205207
.clear()
206208
.type('Basic_condition_P-1')
@@ -217,6 +219,7 @@ describe('Flow creation and editing process', () => {
217219
cy.get('[data-testid=condition-modal]')
218220
.find('[data-testid=tab-basic]')
219221
.click()
222+
cy.wait(600)
220223
cy.get('input[data-testid=condition-name]')
221224
.clear()
222225
.type('Basic_condition_P-2')
@@ -239,6 +242,7 @@ describe('Flow creation and editing process', () => {
239242
cy.get('[data-testid=condition-modal]')
240243
.find('[data-testid=tab-basic]')
241244
.click()
245+
cy.wait(600)
242246
cy.get('input[data-testid=condition-name]')
243247
.clear()
244248
.type('All_of_condition')
@@ -267,6 +271,7 @@ describe('Flow creation and editing process', () => {
267271
cy.get('[data-testid=condition-modal]')
268272
.find('[data-testid=tab-basic]')
269273
.click()
274+
cy.wait(600)
270275
cy.get('input[data-testid=condition-name]')
271276
.clear()
272277
.type('Any_of_condition')
@@ -308,6 +313,7 @@ describe('Flow creation and editing process', () => {
308313
cy.get('[data-testid=condition-modal]')
309314
.find('[data-testid=tab-basic]')
310315
.click()
316+
cy.wait(600)
311317
cy.get('input[data-testid=condition-name]')
312318
.clear()
313319
.type('Exact_match_condition')
@@ -326,6 +332,7 @@ describe('Flow creation and editing process', () => {
326332
cy.get('[data-testid=condition-modal]')
327333
.find('[data-testid=tab-basic]')
328334
.click()
335+
cy.wait(600)
329336
cy.get('input[data-testid=condition-name]')
330337
.clear()
331338
.type('Regexp_condition')
@@ -344,6 +351,7 @@ describe('Flow creation and editing process', () => {
344351
cy.get('[data-testid=condition-modal]')
345352
.find('[data-testid=tab-basic]')
346353
.click()
354+
cy.wait(600)
347355
cy.get('input[data-testid=condition-name]')
348356
.clear()
349357
.type('Not_has_text_condition')
@@ -361,6 +369,7 @@ describe('Flow creation and editing process', () => {
361369
.should('exist')
362370
.find('button[data-testid=add-condition-btn]')
363371
.click()
372+
cy.wait(600)
364373
cy.get('input[data-testid=condition-name]')
365374
.clear()
366375
.type('Python_condition')

frontend/cypress/support/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
Cypress.Commands.add('saveFlow', () => {
4040
cy.get('body').trigger('keydown', {
41-
key: 's',
41+
code: 'KeyS',
4242
ctrlKey: true,
4343
bubbles: true,
4444
})

frontend/src/UI/ScrolledContainer/ScrolledContainer.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.hasScrollbar {
2-
margin-right: -18px;
3-
padding-right: 6px;
2+
margin-right: var(--scrollbar-margin);
3+
padding-right: var(--scrollbar-padding);
44
}
55
.scrolled_container::-webkit-scrollbar {
66
height: 12px;

frontend/src/UI/ScrolledContainer/ScrolledContainer.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import cn from 'classnames'
1111
interface ScrolledContainerProps extends React.PropsWithChildren {
1212
children: ReactNode
1313
className?: string
14+
scrollbarOffset?: number
1415
}
1516

1617
const ScrolledContainer = forwardRef<HTMLDivElement, ScrolledContainerProps>(
17-
({ children, className }, ref) => {
18+
({ children, className, scrollbarOffset = 0 }, ref) => {
1819
const innerRef = useRef<HTMLDivElement>(null)
1920
const containerRef = (ref || innerRef) as RefObject<HTMLDivElement>
2021
const hasScrollbarRef = useRef(false)
@@ -61,11 +62,23 @@ const ScrolledContainer = forwardRef<HTMLDivElement, ScrolledContainerProps>(
6162
// eslint-disable-next-line react-hooks/exhaustive-deps
6263
}, [children])
6364

65+
const mr = `-${12 + scrollbarOffset}px`
66+
const pr = `${scrollbarOffset}px`
67+
6468
return (
6569
<div className={cn('flex max-h-full w-full', className)}>
6670
<div
6771
ref={containerRef}
68-
className='scrolled_container flex-grow overflow-y-auto'
72+
className={cn(
73+
'scrolled_container flex-grow overflow-y-auto',
74+
hasScrollbarRef.current && `mr-[${mr}] pr-[${pr}]`,
75+
)}
76+
style={
77+
{
78+
'--scrollbar-margin': mr,
79+
'--scrollbar-padding': pr,
80+
} as React.CSSProperties
81+
}
6982
>
7083
{children}
7184
</div>

frontend/src/api/llm.tsx

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import {
2+
ILlmConfig,
3+
ILlmConfigs,
4+
IToken,
5+
ITokenFormData,
6+
ITokens,
7+
LlmProviders,
8+
} from '@/types/llmTypes'
9+
import { $v1 } from '.'
10+
11+
export const getLlmProviders = async (): Promise<LlmProviders> => {
12+
try {
13+
const { data } = await $v1.get('/config/providers')
14+
return data
15+
} catch (error) {
16+
console.log(error)
17+
throw error
18+
}
19+
}
20+
21+
export const getLLMTokens = async (): Promise<ITokens> => {
22+
try {
23+
const {
24+
data: { data },
25+
} = await $v1.get('/config/llms/tokens')
26+
27+
return data
28+
} catch (error) {
29+
console.log(error)
30+
throw error
31+
}
32+
}
33+
34+
export const createLLMToken = async (
35+
token: ITokenFormData,
36+
): Promise<string> => {
37+
try {
38+
const {
39+
data: { token_id },
40+
} = await $v1.post(
41+
`/config/llms/token?provider=${token.provider}&token_name=${token.name}&token_value=${token.value}`,
42+
)
43+
return token_id
44+
} catch (error) {
45+
console.log(error)
46+
throw error
47+
}
48+
}
49+
50+
export const updateLLMToken = async (
51+
token_id: string,
52+
newToken: Partial<IToken>,
53+
) => {
54+
try {
55+
const params = new URLSearchParams({ token_id })
56+
57+
if (newToken.name) params.append('new_token_name', newToken.name)
58+
if (newToken.value) params.append('new_token_value', newToken.value)
59+
if (newToken.provider) params.append('new_provider', newToken.provider)
60+
61+
const { data } = await $v1.patch(`/config/llms/token?${params.toString()}`)
62+
63+
return data
64+
} catch (error) {
65+
console.log(error)
66+
throw error
67+
}
68+
}
69+
70+
export const deleteLLMToken = async (token_id: string) => {
71+
try {
72+
const { data } = await $v1.delete(`/config/llms/token?&id=${token_id}`)
73+
return data
74+
} catch (error) {
75+
console.log(error)
76+
throw error
77+
}
78+
}
79+
80+
export const getLlmConfigs = async (): Promise<ILlmConfigs> => {
81+
try {
82+
const {
83+
data: { data },
84+
} = await $v1.get('/config/llms')
85+
return data
86+
} catch (error) {
87+
console.log(error)
88+
throw error
89+
}
90+
}
91+
92+
export const createLlmConfig = async (config: Omit<ILlmConfig, 'id'>) => {
93+
try {
94+
const systemPromptString = config?.system_prompt
95+
? `&system_prompt=${config.system_prompt}`
96+
: ''
97+
const {
98+
data: { config_id },
99+
} = await $v1.post(
100+
`/config/llms?config_name=${config.name}&model_name=${config.model_name}&llm_token_id=${config.token_id}${systemPromptString}`,
101+
)
102+
return config_id
103+
} catch (error) {
104+
console.log(error)
105+
throw error
106+
}
107+
}
108+
109+
export const updateLlmConfig = async (
110+
config_id: string,
111+
newConfig: Partial<ILlmConfig>,
112+
) => {
113+
try {
114+
const params = new URLSearchParams({ config_id })
115+
if (newConfig.name) {
116+
params.append('new_config_name', newConfig.name)
117+
}
118+
if (newConfig.model_name) {
119+
params.append('model_name', newConfig.model_name)
120+
}
121+
if (newConfig.token_id) {
122+
params.append('llm_token_id', newConfig.token_id.toString())
123+
}
124+
if (newConfig.system_prompt) {
125+
params.append('system_prompt', newConfig.system_prompt)
126+
}
127+
128+
const { data } = await $v1.patch(`/config/llms?${params.toString()}`)
129+
return data
130+
} catch (error) {
131+
console.log(error)
132+
throw error
133+
}
134+
}
135+
136+
export const deleteLlmConfig = async (config_id: string) => {
137+
try {
138+
const { data } = await $v1.delete(`config/llms?config_id=${config_id}`)
139+
return data
140+
} catch (error) {
141+
console.log(error)
142+
throw error
143+
}
144+
}
145+
146+
export const getDefaultConfigId = async (): Promise<string> => {
147+
try {
148+
const {
149+
data: { data },
150+
} = await $v1.get(`config/llms/default`)
151+
return data
152+
} catch (error) {
153+
console.log(error)
154+
throw error
155+
}
156+
}
157+
158+
export const setDefaultConfigId = async (id: string) => {
159+
try {
160+
const { data } = await $v1.post(`config/llms/default/?config_id=${id}`)
161+
return data
162+
} catch (error) {
163+
console.log(error)
164+
throw error
165+
}
166+
}

frontend/src/components/footbar/FootBar.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import MonitorIcon from '../../icons/buildmenu/MonitorIcon'
1717
import LocalStorageIcon from '../../icons/footbar/LocalStorageIcon'
1818
import { Logo } from '../../icons/Logo'
1919
import LocalStorage from '../../modals/LocalStorage/LocalStorage'
20-
import { parseSearchParams } from '../../utils'
2120
import { NotificationsWindow } from '../notifications/NotificationsWindow'
2221

2322
const FootBar = memo(() => {
@@ -41,11 +40,10 @@ const FootBar = memo(() => {
4140
(key: Key) => {
4241
const pageKey = key as PageType
4342
setSearchParams({
44-
...parseSearchParams(searchParams),
4543
page: pageKey,
4644
})
4745
},
48-
[searchParams, setSearchParams],
46+
[setSearchParams],
4947
)
5048

5149
return (

0 commit comments

Comments
 (0)