Skip to content

Commit

Permalink
Merge branch 'dev' into fix/sync-with-backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Ramimashkouk committed Dec 16, 2024
1 parent dc29529 commit 3fd9580
Show file tree
Hide file tree
Showing 38 changed files with 829 additions and 253 deletions.
10 changes: 3 additions & 7 deletions .github/workflows/backend_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,9 @@ jobs:
python -m poetry run chatsky.ui init --destination ../ --no-input --overwrite-if-exists
working-directory: backend

- name: Install chatsky-ui into new project poetry environment
run: |
../bin/add_ui_to_toml.sh
working-directory: my_project

- name: run tests
run: |
python -m poetry install --no-root
python -m poetry run pytest ../backend/chatsky_ui/tests/ --verbose
poetry install --with dev -C ../backend
POETRY_ENV=$(poetry env info --path -C ../backend)
$POETRY_ENV/bin/pytest ../backend/chatsky_ui/tests/ --verbose
working-directory: my_project
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Quick Start
## System Requirements
Ensure you have Python version 3.8.1 or higher installed.
Ensure you have Python version 3.9 or higher installed (Excluding 3.9.7).

## Installation
To install the necessary package, run the following command:
To install the package and necessary dependencies, run the following command:
```bash
pip install chatsky-ui
```
Expand Down
13 changes: 6 additions & 7 deletions backend/chatsky_ui/api/api_v1/endpoints/bot.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import asyncio
from typing import Any, Dict, List, Optional, Union

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, WebSocket, WebSocketException, status
from chatsky.messengers.http_interface import HTTP_INTERFACE_PORT
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from httpx import AsyncClient

from chatsky_ui.api import deps
from chatsky_ui.core.config import settings
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.process_manager import BuildManager, ProcessManager, RunManager
from httpx import AsyncClient

router = APIRouter()

Expand Down Expand Up @@ -144,7 +143,7 @@ async def start_run(
build_id: int,
preset: Preset,
background_tasks: BackgroundTasks,
run_manager: RunManager = Depends(deps.get_run_manager)
run_manager: RunManager = Depends(deps.get_run_manager),
) -> Dict[str, Union[str, int]]:
"""Starts a `run` process with the given preset.
Expand Down Expand Up @@ -243,12 +242,12 @@ async def respond(
async with AsyncClient() as client:
try:
response = await client.post(
f"http://localhost:{HTTP_INTERFACE_PORT}/chat",
f"http://localhost:{settings.chatsky_port}/chat",
params={"user_id": user_id, "user_message": user_message},
)
return response.json()
except Exception as e:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"Please check that service's up and running on the port '{HTTP_INTERFACE_PORT}'.",
detail=f"Please check that service's up and running on the port '{settings.chatsky_port}'.",
) from e
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from chatsky_ui.clients.chatsky_client import get_chatsky_conditions
from chatsky_ui.core.config import settings
from chatsky_ui.schemas.code_snippet import CodeSnippet
from chatsky_ui.services.json_converter_new2.logic_component_converter.service_replacer import get_all_classes
from chatsky_ui.services.json_converter.logic_component_converter.service_replacer import get_all_classes
from chatsky_ui.utils.ast_utils import get_imports_from_file

router = APIRouter()
Expand Down
13 changes: 6 additions & 7 deletions backend/chatsky_ui/api/api_v1/endpoints/flows.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
from typing import Dict, Union, Optional
from pathlib import Path
from typing import Dict, Optional, Union

from fastapi import APIRouter, status, HTTPException
from omegaconf import OmegaConf
from dotenv import set_key
from fastapi import APIRouter, HTTPException, status
from git.exc import GitCommandError
from omegaconf import OmegaConf

from chatsky_ui.core.config import settings
from chatsky_ui.core.logger_config import get_logger
from chatsky_ui.db.base import read_conf, write_conf
from chatsky_ui.utils.git_cmd import commit_changes, get_repo
from chatsky_ui.core.logger_config import get_logger
from pathlib import Path
from dotenv import set_key


router = APIRouter()

Expand Down
1 change: 0 additions & 1 deletion backend/chatsky_ui/api/deps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from chatsky_ui.services.process_manager import BuildManager, RunManager


build_manager = BuildManager()


Expand Down
16 changes: 4 additions & 12 deletions backend/chatsky_ui/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,20 @@
import string
import sys
from pathlib import Path
from typing import Optional

import nest_asyncio
import typer
from cookiecutter.main import cookiecutter
from typing_extensions import Annotated
from git import Repo
from typing import Optional
from typing_extensions import Annotated

# Patch nest_asyncio before importing Chatsky
nest_asyncio.apply = lambda: None

from chatsky_ui.core.config import app_runner, settings # noqa: E402
from chatsky_ui.core.logger_config import get_logger # noqa: E402
from chatsky_ui.utils.git_cmd import ( # noqa: E402
commit_changes,
get_repo,
save_built_script_to_git,
save_frontend_graph_to_git,
) # noqa: E402
from chatsky_ui.utils.git_cmd import commit_changes # noqa: E402

cli = typer.Typer(
help="🚀 Welcome to Chatsky-UI!\n\n"
Expand Down Expand Up @@ -108,9 +103,7 @@ def build_scenario(
raise NotADirectoryError(f"Directory {project_dir} doesn't exist")
settings.set_config(work_directory=project_dir)

from chatsky_ui.services.json_converter_new2.pipeline_converter import (
PipelineConverter,
) # pylint: disable=C0415
from chatsky_ui.services.json_converter.pipeline_converter import PipelineConverter # pylint: disable=C0415

pipeline_converter = PipelineConverter()
pipeline_converter(
Expand Down Expand Up @@ -207,7 +200,6 @@ def init(
"https://github.com/deeppavlov/chatsky-ui-template.git",
no_input=no_input,
overwrite_if_exists=overwrite_if_exists,
checkout="add-test",
)
finally:
os.chdir(original_dir)
Expand Down
9 changes: 5 additions & 4 deletions backend/chatsky_ui/core/config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import logging
import os
from pathlib import Path
from typing import Dict

import uvicorn
from dotenv import load_dotenv
import logging
from typing import Dict
from omegaconf import DictConfig, OmegaConf


LOG_LEVELS: Dict[str, int] = {
"critical": logging.CRITICAL,
"error": logging.ERROR,
Expand All @@ -34,6 +33,7 @@ def __init__(self):
self.set_config(
host=os.getenv("HOST", "0.0.0.0"),
port=os.getenv("PORT", "8000"),
chatsky_port=os.getenv("CHATSKY_PORT", "8020"),
log_level=os.getenv("LOG_LEVEL", "info"),
conf_reload=os.getenv("CONF_RELOAD", "false"),
work_directory=".",
Expand All @@ -45,7 +45,7 @@ def set_config(self, **kwargs):
value = Path(value)
elif key == "conf_reload":
value = str(value).lower() in ["true", "yes", "t", "y", "1"]
elif key == "port":
elif key in ["port", "CHATSKY_PORT"]:
value = int(value)
setattr(self, key, value)

Expand Down Expand Up @@ -75,6 +75,7 @@ def save_config(self):
"work_directory": str(self.work_directory),
"host": self.host,
"port": self.port,
"chatsky_port": self.chatsky_port,
"log_level": self.log_level,
"conf_reload": self.conf_reload,
}
Expand Down
2 changes: 1 addition & 1 deletion backend/chatsky_ui/core/logger_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pathlib import Path
from typing import Literal, Optional

from chatsky_ui.core.config import settings, LOG_LEVELS
from chatsky_ui.core.config import LOG_LEVELS, settings


def setup_logging(log_type: Literal["builds", "runs"], id_: int, timestamp: datetime) -> Path:
Expand Down
13 changes: 13 additions & 0 deletions backend/chatsky_ui/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import signal
from contextlib import asynccontextmanager

from fastapi import APIRouter, FastAPI, Response
Expand All @@ -6,15 +7,27 @@

from chatsky_ui import __version__
from chatsky_ui.api.api_v1.api import api_router
from chatsky_ui.api.deps import run_manager
from chatsky_ui.core.config import settings


def signal_handler(self, signum):
global stop_background_task
print("Caught termination signal, shutting down gracefully...")
for process in run_manager.processes.values():
process.to_be_terminated = True


@asynccontextmanager
async def lifespan(app: FastAPI):
if settings.temp_conf.exists():
settings.refresh_work_dir()
signal.signal(signal.SIGINT, signal_handler)
yield

settings.temp_conf.unlink(missing_ok=True)
await run_manager.stop_all()


app = FastAPI(title="DF Designer", version=__version__, lifespan=lifespan)

Expand Down
17 changes: 7 additions & 10 deletions backend/chatsky_ui/schemas/front_graph_components/interface.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
from pydantic import Field, model_validator
from typing import Any
import os
from typing import Any, Dict, Optional

from .base_component import BaseComponent
from typing import Optional, Dict
from dotenv import load_dotenv
import os
from pydantic import Field, model_validator

from chatsky_ui.core.config import settings

from .base_component import BaseComponent

load_dotenv(os.path.join(settings.work_directory, ".env"), override=True)

load_dotenv(os.path.join(settings.work_directory, ".env"))

class Interface(BaseComponent):
model_config = {
"extra": "forbid"
}
model_config = {"extra": "forbid"}

telegram: Optional[Dict[str, Any]] = Field(default=None)
http: Optional[Dict[str, Any]] = Field(default=None)
Expand All @@ -28,7 +26,6 @@ def check_one_not_none(cls, values):

@model_validator(mode="after")
def check_telegram_token(cls, values):
load_dotenv(os.path.join(settings.work_directory, ".env"))
tg_bot_token = os.getenv("TG_BOT_TOKEN")
if values.telegram is not None and not tg_bot_token:
raise ValueError("Telegram token must be provided.")
Expand Down
3 changes: 2 additions & 1 deletion backend/chatsky_ui/schemas/front_graph_components/node.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import List

from pydantic import model_validator

from .base_component import BaseComponent
Expand All @@ -23,7 +24,7 @@ class SlotsNode(Node):
groups: List[dict]

@model_validator(mode="after")
def check_unique_groups_names(cls, values) -> 'SlotsNode':
def check_unique_groups_names(cls, values) -> "SlotsNode":
groups_names = [group["name"] for group in values.groups]
if len(groups_names) != len(set(groups_names)):
raise ValueError(f"Slot groups names should be unique. Got duplicates: {groups_names}")
Expand Down
10 changes: 10 additions & 0 deletions backend/chatsky_ui/services/json_converter/base_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from abc import ABC, abstractmethod


class BaseConverter(ABC):
def __call__(self, *args, **kwargs):
return self._convert()

@abstractmethod
def _convert(self):
raise NotImplementedError
3 changes: 3 additions & 0 deletions backend/chatsky_ui/services/json_converter/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RESPONSES_FILE = "responses"
CONDITIONS_FILE = "conditions"
CUSTOM_FILE = "custom"
72 changes: 72 additions & 0 deletions backend/chatsky_ui/services/json_converter/flow_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from typing import Any, Dict, List, Tuple

from ...schemas.front_graph_components.flow import Flow
from .base_converter import BaseConverter
from .node_converter import InfoNodeConverter, LinkNodeConverter


class FlowConverter(BaseConverter):
NODE_CONVERTERS = {
"default_node": InfoNodeConverter,
"link_node": LinkNodeConverter,
}

def __init__(self, flow: Dict[str, Any]):
self._validate_flow(flow)
self.flow = Flow(
name=flow["name"],
nodes=flow["data"]["nodes"],
edges=flow["data"]["edges"],
)

def __call__(self, *args, **kwargs):
self.mapped_flows = kwargs["mapped_flows"]
self.slots_conf = kwargs["slots_conf"]
self._integrate_edges_into_nodes()
return super().__call__(*args, **kwargs)

def _validate_flow(self, flow: Dict[str, Any]):
if "data" not in flow or "nodes" not in flow["data"] or "edges" not in flow["data"]:
raise ValueError("Invalid flow structure")

def _integrate_edges_into_nodes(self):
def _insert_dst_into_condition(
node: Dict[str, Any], condition_id: str, target_node: Tuple[str, str]
) -> Dict[str, Any]:
for condition in node["data"]["conditions"]:
if condition["id"] == condition_id:
condition["dst"] = target_node
return node

maped_edges = self._map_edges()
nodes = self.flow.nodes.copy()
for edge in maped_edges:
for idx, node in enumerate(nodes):
if node["id"] == edge["source"]:
nodes[idx] = _insert_dst_into_condition(node, edge["sourceHandle"], edge["target"])
self.flow.nodes = nodes

def _map_edges(self) -> List[Dict[str, Any]]:
def _get_flow_and_node_names(target_node):
node_type = target_node["type"]
if node_type == "link_node": # TODO: WHY CONVERTING HERE?
return LinkNodeConverter(target_node)(mapped_flows=self.mapped_flows)
elif node_type == "default_node":
return [self.flow.name, target_node["data"]["name"]]

edges = self.flow.edges.copy()
for edge in edges:
target_id = edge["target"]
target_node = self.mapped_flows[self.flow.name].get(target_id)
if target_node:
edge["target"] = _get_flow_and_node_names(target_node)
return edges

def _convert(self) -> Dict[str, Any]:
converted_flow = {self.flow.name: {}}
for node in self.flow.nodes:
if node["type"] == "default_node":
converted_flow[self.flow.name].update(
{node["data"]["name"]: InfoNodeConverter(node)(slots_conf=self.slots_conf)}
)
return converted_flow
15 changes: 15 additions & 0 deletions backend/chatsky_ui/services/json_converter/interface_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from chatsky_ui.core.config import settings

from ...schemas.front_graph_components.interface import Interface
from .base_converter import BaseConverter


class InterfaceConverter(BaseConverter):
def __init__(self, interface: dict):
self.interface = Interface(**interface)

def _convert(self):
if self.interface.http is not None:
return {"chatsky.messengers.HTTPMessengerInterface": {"port": settings.chatsky_port}}
elif self.interface.telegram is not None:
return {"chatsky.messengers.TelegramInterface": {"token": {"external:os.getenv": "TG_BOT_TOKEN"}}}
Loading

0 comments on commit 3fd9580

Please sign in to comment.