Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/support tg front #93

Merged
merged 35 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6d6818b
feat: Integrate slots
Ramimashkouk Jul 26, 2024
ca676f8
Merge branch 'dev' into feat/integrate-slots-front
MXerFix Sep 10, 2024
606c864
refactor: Update to chatsky 1.0.0rc1
Ramimashkouk Sep 12, 2024
d0c2449
refactor: Use `ast` to deal with cnd&rsp
Ramimashkouk Sep 12, 2024
d5cc8c5
feat: Add slot converstion
Ramimashkouk Sep 13, 2024
cbefcf2
chore: Update converter to chatsky1.0.0rc1 format
Ramimashkouk Sep 13, 2024
4ad5418
chore: Strip messages before sending to ui
Ramimashkouk Sep 13, 2024
ffdb2f8
chore: Update autocomplet feat to 1.0.0rc1
Ramimashkouk Sep 13, 2024
4bcb0b2
fix: Stop all processes when shutting down
Ramimashkouk Sep 13, 2024
a24c7e3
chore: Delete handshaking with ws
Ramimashkouk Sep 13, 2024
8fe351a
style: Black up
Ramimashkouk Sep 13, 2024
8143213
feat: Support telegram interface
Ramimashkouk Sep 14, 2024
1ccea2f
fix: Include automatic version in fastapi app
Ramimashkouk Sep 3, 2024
ebdad14
chore: Check if slots key exists in front graph
Ramimashkouk Sep 17, 2024
48dca4b
fix: Stop process after disconnection only if running
Ramimashkouk Sep 17, 2024
95e78e3
refactor: init new base input elements
MXerFix Sep 20, 2024
755f696
chore: configuration changes
MXerFix Sep 20, 2024
9045a4f
feat: slots implement + modals rework init
MXerFix Sep 20, 2024
5ae111b
chore: icons fixes + modals rework fixes + sidebar&utils improvements
MXerFix Sep 20, 2024
4c5fe26
Merge branch 'feat/integrate-slots-new' into feat/integrate-slots-front
MXerFix Sep 20, 2024
71ff448
fix: slots filling and saving fixes
MXerFix Sep 20, 2024
a530c49
chore: add slot condition settings & rework condition modal & add sha…
MXerFix Sep 23, 2024
2ce3b47
chore: add quiet save handlers
MXerFix Sep 24, 2024
2809b37
chore: slots components refactor & add condition icons
MXerFix Sep 24, 2024
ca3432b
Merge branch 'feat/integrate-slots-front' into feat/support-tg-front
MXerFix Sep 24, 2024
1b4947e
chore: editor fixes
MXerFix Sep 25, 2024
111892f
feat: telegram init
MXerFix Sep 25, 2024
d1fc4b0
fix: slots_node generate fix
MXerFix Sep 25, 2024
c78d99d
chore: build menu tg support improvements
MXerFix Sep 30, 2024
59c0f06
fix: new modals mouseonpane check fix
MXerFix Sep 30, 2024
775eed9
fix: undo redo mouseonpane check fix
MXerFix Sep 30, 2024
da4f4a3
chore: console log delete
MXerFix Sep 30, 2024
3db4f79
fixes: mini fixes
MXerFix Sep 30, 2024
d3006b0
update version
Ramimashkouk Oct 1, 2024
ee009ec
Merge branch 'dev' into feat/support-tg-front
Ramimashkouk Oct 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/chatsky_ui/api/api_v1/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from fastapi import APIRouter

from chatsky_ui.api.api_v1.endpoints import bot, config, chatsky_services, flows
from chatsky_ui.api.api_v1.endpoints import bot, chatsky_services, config, flows
from chatsky_ui.core.config import settings

api_router = APIRouter()
Expand Down
2 changes: 1 addition & 1 deletion backend/chatsky_ui/api/api_v1/endpoints/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from chatsky_ui.api import deps
from chatsky_ui.schemas.pagination import Pagination
from chatsky_ui.schemas.preset import Preset
from chatsky_ui.schemas.process_status import Status
from chatsky_ui.services.index import Index
from chatsky_ui.services.process_manager import BuildManager, ProcessManager, RunManager
from chatsky_ui.services.websocket_manager import WebSocketManager
from chatsky_ui.schemas.process_status import Status

router = APIRouter()

Expand Down
1 change: 0 additions & 1 deletion backend/chatsky_ui/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ def init(
"https://github.com/Ramimashkouk/df_d_template.git",
no_input=no_input,
overwrite_if_exists=overwrite_if_exists,
checkout="feat/add-slots2"
)
finally:
os.chdir(original_dir)
2 changes: 1 addition & 1 deletion backend/chatsky_ui/services/condition_finder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ast
from ast import NodeTransformer
from typing import List, Dict
from typing import Dict, List

from chatsky_ui.core.logger_config import get_logger

Expand Down
18 changes: 5 additions & 13 deletions backend/chatsky_ui/services/json_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
Converts a user project's frontend graph to a script understandable by Chatsky json-importer.
"""
import ast
from collections import defaultdict
from pathlib import Path
from typing import List, Optional, Tuple
from collections import defaultdict

from omegaconf.dictconfig import DictConfig

Expand All @@ -24,9 +24,6 @@
PRE_TRANSITION = "PRE_TRANSITION"


PRE_TRANSITION = "PRE_TRANSITION"


def _get_db_paths(build_id: int) -> Tuple[Path, Path, Path, Path]:
"""Get paths to frontend graph, chatsky script, and chatsky custom conditions files."""
frontend_graph_path = settings.frontend_flows_path
Expand Down Expand Up @@ -208,7 +205,7 @@ def map_interface(interface: DictConfig) -> dict:
if not isinstance(interface, DictConfig):
raise ValueError(f"Interface must be a dictionary. Got: {type(interface)}")
keys = interface.keys()
if len(keys)!=1:
if len(keys) != 1:
raise ValueError("There must be only one key in the interface")

key = next(iter(keys))
Expand All @@ -217,18 +214,13 @@ def map_interface(interface: DictConfig) -> dict:
raise ValueError("Token keyworkd is not provided for telegram interface")
if not interface[key]["token"]:
raise ValueError("Token is not provided for telegram interface")
return {
"chatsky.messengers.telegram.LongpollingInterface": {
"token": interface[key]["token"]
}
}
return {"chatsky.messengers.telegram.LongpollingInterface": {"token": interface[key]["token"]}}
if key == "cli":
return {
"chatsky.messengers.console.CLIMessengerInterface": {}
}
return {"chatsky.messengers.console.CLIMessengerInterface": {}}
else:
raise ValueError(f"Unknown interface: {key}")


async def converter(build_id: int) -> None:
"""Translate frontend flow script into chatsky script."""
frontend_graph_path, script_path, custom_conditions_file, custom_responses_file = _get_db_paths(build_id)
Expand Down
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "chatsky-ui"
version = "0.2.0"
version = "0.3.0"
description = "Chatsky-UI is GUI for Chatsky Framework, that is a free and open-source software stack for creating chatbots, released under the terms of Apache License 2.0."
license = "Apache-2.0"
authors = [
Expand Down
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os
import sys
from chatsky_ui import __version__

sys.path.insert(0, os.path.abspath(".."))
# -- Project information -----------------------------------------------------
Expand All @@ -13,7 +14,7 @@
project = 'Chatsky-UI'
copyright = '2024, Denis Kuznetsov, Maks Rogatkin, Rami Mashkouk'
author = 'Denis Kuznetsov, Maks Rogatkin, Rami Mashkouk'
release = '0.2.0'
release = __version__

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/maste r/usage/configuration.html#general-configuration
Expand Down
Binary file modified frontend/bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@nextui-org/react": "^2.2.9",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-select": "^2.1.1",
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/UI/Code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import classNames from 'classnames'
import React from 'react'

const Code = ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div className={classNames(
"inline text-foreground text-xs bg-table-background rounded-md px-1 py-0.5 font-mono whitespace-nowrap",
props.className
)} {...props} >{children}</div>
)
}

export default Code
129 changes: 129 additions & 0 deletions frontend/src/UI/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
import classNames from "classnames"
import React, { forwardRef } from "react"

export type DropdownItemType = {
label: string
value: string
className?: string
icon?: React.ReactNode
disabled?: boolean
shortcut?: string
onClick?: () => void
}

export type DropdownGroupType = {
title?: string
items: DropdownItemType[]
}

interface DropdownProps {
groups: DropdownGroupType[]
onSelect: (value: string) => void
triggerContent: React.ReactNode
}

const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
({ groups, onSelect, triggerContent }, ref) => {
// const [highlightedIndex, setHighlightedIndex] = useState<number | null>(null)
// const [currentGroupIndex, setCurrentGroupIndex] = useState<number | null>(null)

// const handleKeyDown = (event: React.KeyboardEvent) => {
// const allItems = groups.flatMap((group) => group.items)

// if (event.key === "ArrowDown") {
// if (highlightedIndex === null || currentGroupIndex === null) {
// setHighlightedIndex(0)
// setCurrentGroupIndex(0)
// } else {
// setHighlightedIndex((prev) =>
// prev === null ? 0 : Math.min(prev + 1, allItems.length - 1)
// )
// }
// } else if (event.key === "ArrowUp") {
// setHighlightedIndex((prev) => (prev === null ? 0 : Math.max(prev - 1, 0)))
// } else if (event.key === "Enter" && highlightedIndex !== null && currentGroupIndex !== null) {
// const selectedItem = allItems[highlightedIndex]
// if (!selectedItem.disabled) {
// onSelect(selectedItem.value)
// selectedItem.onClick?.()
// }
// }
// }

return (
<DropdownMenu.Root>
{/* Триггер для Dropdown */}
<DropdownMenu.Trigger
autoFocus={false}
asChild>
<div
autoFocus={false}
className='focus:outline-1 outline-border rounded-lg group'
tabIndex={0}>
{triggerContent}
</div>
</DropdownMenu.Trigger>

{/* Контент Dropdown */}
<DropdownMenu.Portal>
<DropdownMenu.Content
onCloseAutoFocus={(event) => {
event.preventDefault()
}}
asChild
key={"dropdown-content"}
sideOffset={5}
align='start'
side='bottom'
className='z-[99] min-w-56 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out data-[state=open]:zoom-in'>
<div
className={classNames(
"bg-background border border-border rounded-xl p-2 origin-top-left"
)}>
{groups.map((group, groupIndex) => (
<DropdownMenu.Group
title={group.title}
key={`group-${groupIndex}`}
className='grid gap-1'>
{group.items.map((item, index) => (
<DropdownMenu.Item
key={item.value}
onSelect={() => {
if (!item.disabled) {
onSelect(item.value)
item.onClick?.()
}
}}
disabled={item.disabled}
className={classNames(
`flex items-center justify-between px-3 py-2 rounded-lg outline-none transition-colors !duration-150 data-[highlighted]:bg-bg-secondary border border-transparent data-[highlighted]:border-border ${item.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} `,
item.className
)}>
{/* Иконка */}
<div className='flex items-center'>
{item.icon && <span className='mr-2'>{item.icon}</span>}
<span>{item.label}</span>
</div>
{/* Шорткат */}
{item.shortcut && (
<span className='ml-auto text-gray-400'>{item.shortcut}</span>
)}
</DropdownMenu.Item>
))}

{/* Разделитель между группами */}
{groupIndex < groups.length - 1 && (
<DropdownMenu.Separator className='my-2 h-px bg-input-border' />
)}
</DropdownMenu.Group>
))}
</div>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)
}
)

export default Dropdown
9 changes: 9 additions & 0 deletions frontend/src/UI/Loader/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import classNames from "classnames"
import React from "react"
import "./loader.css"

const Loader = ({ className }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={classNames("w-5 h-5 inline-block border border-foreground !border-b-transparent rounded-full loader-rotation", className)}></span>
}

export default Loader
12 changes: 12 additions & 0 deletions frontend/src/UI/Loader/loader.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.loader-rotation {
animation: rotation 1s linear infinite;
}

@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
14 changes: 14 additions & 0 deletions frontend/src/UI/MotionContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { HTMLMotionProps, motion } from "framer-motion"
const MotionContent = ({ children, ...props }: HTMLMotionProps<"div">) => {
return (
<motion.div
{...props}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}>
{children}
</motion.div>
)
}

export default MotionContent
14 changes: 13 additions & 1 deletion frontend/src/api/flows.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { interfaceType } from "@/contexts/flowContext"
import { $v1 } from "."
import { FlowType } from "../types/FlowTypes"
import { ParsedSlot } from "../utils"
Expand All @@ -9,10 +10,21 @@ export const get_flows = async (): Promise<GetFlowsResponseType> => {
return (await $v1.get("/flows")).data
}

export const save_flows = async (flows: FlowType[], slots?: Record<string, ParsedSlot> | null): Promise<SaveFlowsResponseType> => {
export const save_flows = async (flows: FlowType[], _interface: interfaceType, slots?: Record<string, ParsedSlot> | null): Promise<SaveFlowsResponseType> => {
// const hasValidSlots = slots && Object.values(slots).some(slot => Object.keys(slot).length > 0);
const _i = _interface.interface === "tg" ? {
telegram: {
token: _interface.token
}
} : {
cli: {}
}
const json = {
flows,
slots: slots ?? {},
interface: {
..._i
}
}
return (await $v1.post("/flows", json)).data
}
Loading
Loading