From dce860228c2ba3dc814597319d645dcc5dc536cd Mon Sep 17 00:00:00 2001 From: Kseniya Petukhova <42929290+Kpetyxova@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:33:25 +0300 Subject: [PATCH 01/11] fixed source of combined classifier in dream_light pipeline (#490) --- assistant_dists/dream_light/pipeline_conf.json | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/assistant_dists/dream_light/pipeline_conf.json b/assistant_dists/dream_light/pipeline_conf.json index d235fe1aac..21a0f550a9 100644 --- a/assistant_dists/dream_light/pipeline_conf.json +++ b/assistant_dists/dream_light/pipeline_conf.json @@ -105,9 +105,8 @@ "state_manager_method": "add_annotation", "is_enabled": true, "source": { - "directory": "annotators/combined_classification_lightweight", - "container": "combined-classification-lightweight", - "endpoint": "model" + "component": "components/dflkgjdlkh342r9ndf.yml", + "service": "annotators/combined_classification_lightweight/service_configs/combined-classification-lightweight" } } }, @@ -238,9 +237,8 @@ "state_manager_method": "add_hypothesis_annotation_batch", "is_enabled": true, "source": { - "directory": "annotators/combined_classification_lightweight", - "container": "combined-classification-lightweight", - "endpoint": "batch_model" + "component": "components/dflkgjdlkh342r9ndf.yml", + "service": "annotators/combined_classification_lightweight/service_configs/combined-classification-lightweight" } } }, From 026ca9e3c7f5c1a1ec9618c99fbe9028969eeb8d Mon Sep 17 00:00:00 2001 From: "Dilyara Zharikova (Baymurzina)" Date: Sat, 17 Jun 2023 16:26:00 +0300 Subject: [PATCH 02/11] fix: add chatgpt-16k to universal (#492) --- .../universal_prompted_assistant/dev.yml | 6 +++++ .../docker-compose.override.yml | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/assistant_dists/universal_prompted_assistant/dev.yml b/assistant_dists/universal_prompted_assistant/dev.yml index 2d211d55e3..53bcb27708 100644 --- a/assistant_dists/universal_prompted_assistant/dev.yml +++ b/assistant_dists/universal_prompted_assistant/dev.yml @@ -66,6 +66,12 @@ services: - "./common:/src/common" ports: - 8160:8160 + openai-api-chatgpt-16k: + volumes: + - "./services/openai_api_lm:/src" + - "./common:/src/common" + ports: + - 8167:8167 dff-universal-prompted-skill: volumes: - "./skills/dff_universal_prompted_skill:/src" diff --git a/assistant_dists/universal_prompted_assistant/docker-compose.override.yml b/assistant_dists/universal_prompted_assistant/docker-compose.override.yml index 005266a4e3..bf8e84b406 100644 --- a/assistant_dists/universal_prompted_assistant/docker-compose.override.yml +++ b/assistant_dists/universal_prompted_assistant/docker-compose.override.yml @@ -5,7 +5,7 @@ services: WAIT_HOSTS: "sentseg:8011, ranking-based-response-selector:8002, combined-classification:8087, sentence-ranker:8128, transformers-lm-gptjt:8161, transformers-lm-oasst12b:8158, openai-api-chatgpt:8145, openai-api-davinci3:8131, - openai-api-gpt4:8159, openai-api-gpt4-32k:8160, + openai-api-gpt4:8159, openai-api-gpt4-32k:8160, openai-api-chatgpt-16k:8167, dff-universal-prompted-skill:8147" WAIT_HOSTS_TIMEOUT: ${WAIT_TIMEOUT:-1000} @@ -206,6 +206,26 @@ services: reservations: memory: 100M + openai-api-chatgpt-16k: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8167 + SERVICE_NAME: openai_api_chatgpt_16k + PRETRAINED_MODEL_NAME_OR_PATH: gpt-3.5-turbo-16k + context: . + dockerfile: ./services/openai_api_lm/Dockerfile + command: flask run -h 0.0.0.0 -p 8167 + environment: + - CUDA_VISIBLE_DEVICES=0 + - FLASK_APP=server + deploy: + resources: + limits: + memory: 100M + reservations: + memory: 100M + dff-universal-prompted-skill: env_file: [ .env ] build: From 04dbfbbb076b149b65f3a982343d846a89bc8c7f Mon Sep 17 00:00:00 2001 From: ruthenian8 Date: Mon, 19 Jun 2023 09:34:25 +0300 Subject: [PATCH 03/11] Upd/dff/template skill (#303) * add dff_release folder; add minimal changes to server.py; modify dockerfile to use dff_release; modify tests for use with message class; ensure the passing of tests * rename actor.py to serializer.py, reimplement serialization * edit requirements.txt; use MultiMessage from dff * rename dff_release to dff_api_v1; change dockerfile to import dff_api_v1; remove nulls from skill output; exclude nulls from test data; add run_dff function to serializer file * add DreamMessage and DreamMultiMessage classes; process in serializer; make message fields optional to discard null values; edit main.py * remove redundant imports from serializer.py && main.py * remove DreamMultiMessage * Move Pre_transition_processing from node_1 to start_node * apply lint * Add comments to main.py; remove actor instance * apply lint * update requirements: define versions; update import order in main * do minor fixes * move slot filling to a separate integration.response module * change function signature. successful building of dream skill * fix codestyle * merge upstream dev: * Use new dff version in the requirements; --------- Co-authored-by: dilyararimovna --- common/dff_api_v1/integration/condition.py | 289 +++++++++++++++ common/dff_api_v1/integration/context.py | 333 ++++++++++++++++++ common/dff_api_v1/integration/facts_utils.py | 316 +++++++++++++++++ common/dff_api_v1/integration/message.py | 9 + common/dff_api_v1/integration/processing.py | 92 +++++ common/dff_api_v1/integration/response.py | 19 + common/dff_api_v1/integration/serializer.py | 175 +++++++++ common/dff_api_v1/requirements.txt | 13 + .../dff_science_skill/tests/link_to_in.json | 3 + skills/dff_template_skill/Dockerfile | 2 +- .../dff_template_skill/scenario/condition.py | 9 +- skills/dff_template_skill/scenario/main.py | 115 +++--- .../dff_template_skill/scenario/response.py | 4 +- skills/dff_template_skill/server.py | 9 +- .../tests/lets_talk_out.json | 60 ++-- 15 files changed, 1354 insertions(+), 94 deletions(-) create mode 100644 common/dff_api_v1/integration/condition.py create mode 100644 common/dff_api_v1/integration/context.py create mode 100644 common/dff_api_v1/integration/facts_utils.py create mode 100644 common/dff_api_v1/integration/message.py create mode 100644 common/dff_api_v1/integration/processing.py create mode 100644 common/dff_api_v1/integration/response.py create mode 100644 common/dff_api_v1/integration/serializer.py create mode 100644 common/dff_api_v1/requirements.txt diff --git a/common/dff_api_v1/integration/condition.py b/common/dff_api_v1/integration/condition.py new file mode 100644 index 0000000000..d069279fa3 --- /dev/null +++ b/common/dff_api_v1/integration/condition.py @@ -0,0 +1,289 @@ +import logging +import re + +from nltk.stem import WordNetLemmatizer + +from dff.script import Context +from dff.pipeline import Pipeline + +import common.greeting as common_greeting +import common.utils as common_utils +import common.universal_templates as universal_templates +import common.dff_api_v1.integration.context as int_ctx +from common.acknowledgements import GENERAL_ACKNOWLEDGEMENTS +from common.constants import CAN_CONTINUE_SCENARIO, CAN_NOT_CONTINUE +from .facts_utils import provide_facts_request + +logger = logging.getLogger(__name__) + +wnl = WordNetLemmatizer() + + +# vars is described in README.md + + +def was_clarification_request(ctx: Context, _) -> bool: + flag = ctx.misc["agent"]["clarification_request_flag"] if not ctx.validation else False + logger.debug(f"was_clarification_request = {flag}") + return bool(flag) + + +def is_opinion_request(ctx: Context, pipeline: Pipeline) -> bool: + flag = common_utils.is_opinion_request(int_ctx.get_last_human_utterance(ctx, pipeline)) + logger.debug(f"is_opinion_request = {flag}") + return bool(flag) + + +def is_opinion_expression(ctx: Context, pipeline: Pipeline) -> bool: + flag = common_utils.is_opinion_expression(int_ctx.get_last_human_utterance(ctx, pipeline)) + logger.debug(f"is_opinion_expression = {flag}") + return bool(flag) + + +def is_previous_turn_dff_suspended(ctx: Context, _) -> bool: + flag = ctx.misc["agent"].get("previous_turn_dff_suspended", False) if not ctx.validation else False + logger.debug(f"is_previous_turn_dff_suspended = {flag}") + return bool(flag) + + +def is_current_turn_dff_suspended(ctx: Context, _) -> bool: + flag = ctx.misc["agent"].get("current_turn_dff_suspended", False) if not ctx.validation else False + logger.debug(f"is_current_turn_dff_suspended = {flag}") + return bool(flag) + + +def is_switch_topic(ctx: Context, pipeline: Pipeline) -> bool: + flag = universal_templates.is_switch_topic(int_ctx.get_last_human_utterance(ctx, pipeline)) + logger.debug(f"is_switch_topic = {flag}") + return bool(flag) + + +def is_question(ctx: Context, pipeline: Pipeline) -> bool: + text = int_ctx.get_last_human_utterance(ctx, pipeline)["text"] + flag = common_utils.is_question(text) + logger.debug(f"is_question = {flag}") + return bool(flag) + + +def is_lets_chat_about_topic_human_initiative(ctx: Context, pipeline: Pipeline) -> bool: + flag = universal_templates.if_chat_about_particular_topic( + int_ctx.get_last_human_utterance(ctx, pipeline), int_ctx.get_last_bot_utterance(ctx, pipeline) + ) + logger.debug(f"is_lets_chat_about_topic_human_initiative = {flag}") + return bool(flag) + + +def is_lets_chat_about_topic(ctx: Context, pipeline: Pipeline) -> bool: + flag = is_lets_chat_about_topic_human_initiative(ctx, pipeline) + + last_human_uttr = int_ctx.get_last_human_utterance(ctx, pipeline) + last_bot_uttr_text = int_ctx.get_last_bot_utterance(ctx, pipeline)["text"] + is_bot_initiative = bool(re.search(universal_templates.COMPILE_WHAT_TO_TALK_ABOUT, last_bot_uttr_text)) + flag = flag or (is_bot_initiative and not common_utils.is_no(last_human_uttr)) + logger.debug(f"is_lets_chat_about_topic = {flag}") + return bool(flag) + + +def is_begin_of_dialog(ctx: Context, pipeline: Pipeline, begin_dialog_n=10) -> bool: + flag = int_ctx.get_human_utter_index(ctx, pipeline) < begin_dialog_n + logger.debug(f"is_begin_of_dialog = {flag}") + return bool(flag) + + +def is_interrupted(ctx: Context, pipeline: Pipeline) -> bool: + flag = ( + int_ctx.get_human_utter_index(ctx, pipeline) - int_ctx.get_previous_human_utter_index(ctx, pipeline) + ) != 1 and not was_clarification_request(ctx, pipeline) + logger.debug(f"is_interrupted = {flag}") + return bool(flag) + + +def is_long_interrupted(ctx: Context, pipeline: Pipeline, how_long=3) -> bool: + flag = ( + int_ctx.get_human_utter_index(ctx, pipeline) - int_ctx.get_previous_human_utter_index(ctx, pipeline) + ) > how_long and not was_clarification_request(ctx, pipeline) + logger.debug(f"is_long_interrupted = {flag}") + return bool(flag) + + +def is_new_human_entity(ctx: Context, pipeline: Pipeline) -> bool: + new_entities = int_ctx.get_new_human_labeled_noun_phrase(ctx, pipeline) + flag = bool(new_entities) + logger.debug(f"is_new_human_entity = {flag}") + return bool(flag) + + +def is_last_state(ctx: Context, pipeline: Pipeline, state) -> bool: + flag = False + if not ctx.validation: + history = list(int_ctx.get_history(ctx, pipeline).items()) + if history: + history_sorted = sorted(history, key=lambda x: x[0]) + last_state = history_sorted[-1][1] + if last_state == state: + flag = True + return bool(flag) + + +def is_first_time_of_state(ctx: Context, pipeline: Pipeline, state) -> bool: + flag = state not in list(int_ctx.get_history(ctx, pipeline).values()) + logger.debug(f"is_first_time_of_state {state} = {flag}") + return bool(flag) + + +def if_was_prev_active(ctx: Context, pipeline: Pipeline) -> bool: + flag = False + skill_uttr_indices = set(int_ctx.get_history(ctx, pipeline).keys()) + if not ctx.validation: + human_uttr_index = str(ctx.misc["agent"]["human_utter_index"] - 1) + if human_uttr_index in skill_uttr_indices: + flag = True + return bool(flag) + + +def is_plural(word) -> bool: + lemma = wnl.lemmatize(word, "n") + plural = True if word is not lemma else False + return plural + + +def is_first_our_response(ctx: Context, pipeline: Pipeline) -> bool: + flag = len(list(int_ctx.get_history(ctx, pipeline).values())) == 0 + logger.debug(f"is_first_our_response = {flag}") + return bool(flag) + + +def is_no_human_abandon(ctx: Context, pipeline: Pipeline) -> bool: + """Is dialog breakdown in human utterance or no. Uses MIDAS hold/abandon classes.""" + midas_classes = common_utils.get_intents(int_ctx.get_last_human_utterance(ctx, pipeline), which="midas") + if "abandon" not in midas_classes: + return True + return False + + +def no_special_switch_off_requests(ctx: Context, pipeline: Pipeline) -> bool: + """Function to determine if + - user didn't asked to switch topic, + - user didn't ask to talk about something particular, + - user didn't requested high priority intents (like what_is_your_name) + """ + intents_by_catcher = common_utils.get_intents( + int_ctx.get_last_human_utterance(ctx, pipeline), probs=False, which="intent_catcher" + ) + is_high_priority_intent = any([intent not in common_utils.service_intents for intent in intents_by_catcher]) + is_switch = is_switch_topic(ctx, pipeline) + is_lets_chat = is_lets_chat_about_topic_human_initiative(ctx, pipeline) + + if not (is_high_priority_intent or is_switch or is_lets_chat): + return True + return False + + +def no_requests(ctx: Context, pipeline: Pipeline) -> bool: + """Function to determine if + - user didn't asked to switch topic, + - user didn't ask to talk about something particular, + - user didn't requested high priority intents (like what_is_your_name) + - user didn't requested any special intents + - user didn't ask questions + """ + contain_no_special_requests = no_special_switch_off_requests(ctx, pipeline) + + request_intents = [ + "opinion_request", + "topic_switching", + "lets_chat_about", + "what_are_you_talking_about", + "Information_RequestIntent", + "Topic_SwitchIntent", + "Opinion_RequestIntent", + ] + intents = common_utils.get_intents(int_ctx.get_last_human_utterance(ctx, pipeline), which="all") + is_not_request_intent = all([intent not in request_intents for intent in intents]) + is_no_question = "?" not in int_ctx.get_last_human_utterance(ctx, pipeline)["text"] + + if contain_no_special_requests and is_not_request_intent and is_no_question: + return True + return False + + +def is_yes_vars(ctx: Context, pipeline: Pipeline) -> bool: + flag = True + flag = flag and common_utils.is_yes(int_ctx.get_last_human_utterance(ctx, pipeline)) + return bool(flag) + + +def is_no_vars(ctx: Context, pipeline: Pipeline) -> bool: + flag = True + flag = flag and common_utils.is_no(int_ctx.get_last_human_utterance(ctx, pipeline)) + return bool(flag) + + +def is_do_not_know_vars(ctx: Context, pipeline: Pipeline) -> bool: + flag = True + flag = flag and common_utils.is_donot_know(int_ctx.get_last_human_utterance(ctx, pipeline)) + return bool(flag) + + +def is_passive_user(ctx: Context, pipeline: Pipeline, passive_threshold=3, history_len=2) -> bool: + """Check history_len last human utterances on the number of tokens. + If number of tokens in ALL history_len uterances is less or equal than the given threshold, + then consider user passive - return True. + """ + user_utterances = int_ctx.get_human_utterances(ctx, pipeline)[-history_len:] + user_utterances = [utt["text"] for utt in user_utterances] + + uttrs_lens = [len(uttr.split()) <= passive_threshold for uttr in user_utterances] + if all(uttrs_lens): + return True + return False + + +def get_not_used_and_save_sentiment_acknowledgement(ctx: Context, pipeline: Pipeline, sentiment=None, lang="EN"): + if sentiment is None: + sentiment = int_ctx.get_human_sentiment(ctx, pipeline) + if is_yes_vars(ctx, pipeline) or is_no_vars(ctx, pipeline): + sentiment = "neutral" + + shared_memory = int_ctx.get_shared_memory(ctx, pipeline) + last_acknowledgements = shared_memory.get("last_acknowledgements", []) + + ack = common_utils.get_not_used_template( + used_templates=last_acknowledgements, all_templates=GENERAL_ACKNOWLEDGEMENTS[lang][sentiment] + ) + + used_acks = last_acknowledgements + [ack] + int_ctx.save_to_shared_memory(ctx, pipeline, last_acknowledgements=used_acks[-2:]) + return ack + + +def set_conf_and_can_cont_by_universal_policy(ctx: Context, pipeline: Pipeline): + DIALOG_BEGINNING_START_CONFIDENCE = 0.98 + DIALOG_BEGINNING_CONTINUE_CONFIDENCE = 0.9 + DIALOG_BEGINNING_SHORT_ANSWER_CONFIDENCE = 0.98 + MIDDLE_DIALOG_START_CONFIDENCE = 0.7 + + if not is_begin_of_dialog(ctx, pipeline, begin_dialog_n=10): + confidence = 0.0 + can_continue_flag = CAN_NOT_CONTINUE + elif is_first_our_response(ctx, pipeline): + confidence = DIALOG_BEGINNING_START_CONFIDENCE + can_continue_flag = CAN_CONTINUE_SCENARIO + elif not is_interrupted(ctx, pipeline) and common_greeting.dont_tell_you_answer( + int_ctx.get_last_human_utterance(ctx, pipeline) + ): + confidence = DIALOG_BEGINNING_SHORT_ANSWER_CONFIDENCE + can_continue_flag = CAN_CONTINUE_SCENARIO + elif not is_interrupted(ctx, pipeline): + confidence = DIALOG_BEGINNING_CONTINUE_CONFIDENCE + can_continue_flag = CAN_CONTINUE_SCENARIO + else: + confidence = MIDDLE_DIALOG_START_CONFIDENCE + can_continue_flag = CAN_CONTINUE_SCENARIO + + int_ctx.set_can_continue(ctx, pipeline, can_continue_flag) + int_ctx.set_confidence(ctx, pipeline, confidence) + + +def facts(ctx, pipeline): + return provide_facts_request(ctx, pipeline) diff --git a/common/dff_api_v1/integration/context.py b/common/dff_api_v1/integration/context.py new file mode 100644 index 0000000000..a05816ef8f --- /dev/null +++ b/common/dff_api_v1/integration/context.py @@ -0,0 +1,333 @@ +import logging +import os +import random + +from dff.script import Context +from dff.pipeline import Pipeline + +import common.constants as common_constants +import common.link as common_link +import common.news as common_news +import common.utils as common_utils + +logger = logging.getLogger(__name__) +SERVICE_NAME = os.getenv("SERVICE_NAME") + + +NEWS_API_ANNOTATOR_URL = os.getenv("NEWS_API_ANNOTATOR_URL") + + +def get_new_human_labeled_noun_phrase(ctx: Context, pipeline: Pipeline) -> list: + return ( + [] + if ctx.validation + else ( + get_last_human_utterance(ctx, pipeline).get("annotations", {}).get("cobot_entities", {}).get("entities", []) + ) + ) + + +def get_human_sentiment(ctx: Context, pipeline: Pipeline, negative_threshold=0.5, positive_threshold=0.333) -> str: + sentiment_probs = ( + None if ctx.validation else common_utils.get_sentiment(get_last_human_utterance(ctx, pipeline), probs=True) + ) + if sentiment_probs and isinstance(sentiment_probs, dict): + max_sentiment_prob = max(sentiment_probs.values()) + max_sentiments = [ + sentiment for sentiment in sentiment_probs if sentiment_probs[sentiment] == max_sentiment_prob + ] + if max_sentiments: + max_sentiment = max_sentiments[0] + return_negative = max_sentiment == "negative" and max_sentiment_prob >= negative_threshold + return_positive = max_sentiment == "positive" and max_sentiment_prob >= positive_threshold + if return_negative or return_positive: + return max_sentiment + return "neutral" + + +def get_cross_state(ctx: Context, _, service_name=SERVICE_NAME.replace("-", "_")) -> dict: + return {} if ctx.validation else ctx.misc["agent"]["dff_shared_state"]["cross_states"].get(service_name, {}) + + +def save_cross_state(ctx: Context, _, service_name=SERVICE_NAME.replace("-", "_"), new_state={}): + if not ctx.validation: + ctx.misc["agent"]["dff_shared_state"]["cross_states"][service_name] = new_state + + +def get_cross_link(ctx: Context, pipeline: Pipeline, service_name=SERVICE_NAME.replace("-", "_")) -> dict: + links = {} if ctx.validation else ctx.misc["agent"]["dff_shared_state"]["cross_links"].get(service_name, {}) + cur_human_index = get_human_utter_index(ctx, pipeline) + cross_link = [cross_link for human_index, cross_link in links.items() if (cur_human_index - int(human_index)) == 1] + cross_link = cross_link[0] if cross_link else {} + return cross_link + + +def set_cross_link( + ctx: Context, + pipeline: Pipeline, + to_service_name, + cross_link_additional_data={}, + from_service_name=SERVICE_NAME.replace("-", "_"), +): + cur_human_index = get_human_utter_index(ctx, pipeline) + if not ctx.validation: + ctx.misc["agent"]["dff_shared_state"]["cross_links"][to_service_name] = { + cur_human_index: { + "from_service": from_service_name, + **cross_link_additional_data, + } + } + + +def reset_response_parts(ctx: Context, _): + if not ctx.validation and "response_parts" in ctx.misc["agent"]: + del ctx.misc["agent"]["response_parts"] + + +def add_parts_to_response_parts(ctx: Context, _, parts=[]): + response_parts = set([] if ctx.validation else ctx.misc["agent"].get("response_parts", [])) + response_parts.update(parts) + if not ctx.validation: + ctx.misc["agent"]["response_parts"] = sorted(list(response_parts)) + + +def set_acknowledgement_to_response_parts(ctx: Context, pipeline: Pipeline): + reset_response_parts(ctx, pipeline) + add_parts_to_response_parts(ctx, pipeline, parts=["acknowledgement"]) + + +def add_acknowledgement_to_response_parts(ctx: Context, pipeline: Pipeline): + if not ctx.validation and ctx.misc["agent"].get("response_parts") is None: + add_parts_to_response_parts(ctx, pipeline, parts=["body"]) + add_parts_to_response_parts(ctx, pipeline, parts=["acknowledgement"]) + + +def set_body_to_response_parts(ctx: Context, pipeline: Pipeline): + reset_response_parts(ctx, pipeline) + add_parts_to_response_parts(ctx, pipeline, parts=["body"]) + + +def add_body_to_response_parts(ctx: Context, pipeline: Pipeline): + add_parts_to_response_parts(ctx, pipeline, parts=["body"]) + + +def set_prompt_to_response_parts(ctx: Context, pipeline: Pipeline): + reset_response_parts(ctx, pipeline) + add_parts_to_response_parts(ctx, pipeline, parts=["prompt"]) + + +def add_prompt_to_response_parts(ctx: Context, pipeline: Pipeline): + add_parts_to_response_parts(ctx, pipeline, parts=["prompt"]) + + +def get_shared_memory(ctx: Context, _) -> dict: + return {} if ctx.validation else ctx.misc["agent"]["shared_memory"] + + +def get_used_links(ctx: Context, _) -> dict: + return {} if ctx.validation else ctx.misc["agent"]["used_links"] + + +def get_age_group(ctx: Context, _) -> dict: + return {} if ctx.validation else ctx.misc["agent"]["age_group"] + + +def set_age_group(ctx: Context, _, set_age_group): + if not ctx.validation: + ctx.misc["agent"]["age_group"] = set_age_group + + +def get_disliked_skills(ctx: Context, _) -> list: + return [] if ctx.validation else ctx.misc["agent"]["disliked_skills"] + + +def get_human_utter_index(ctx: Context, _) -> int: + return 0 if ctx.validation else ctx.misc["agent"]["human_utter_index"] + + +def get_previous_human_utter_index(ctx: Context, _) -> int: + return 0 if ctx.validation else ctx.misc["agent"]["previous_human_utter_index"] + + +def get_dialog(ctx: Context, _) -> dict: + return {} if ctx.validation else ctx.misc["agent"]["dialog"] + + +def get_utterances(ctx: Context, _) -> dict: + return [] if ctx.validation else ctx.misc["agent"]["dialog"]["utterances"] + + +def get_human_utterances(ctx: Context, _) -> dict: + return [] if ctx.validation else ctx.misc["agent"]["dialog"]["human_utterances"] + + +def get_last_human_utterance(ctx: Context, _) -> dict: + return {"text": "", "annotations": {}} if ctx.validation else ctx.misc["agent"]["dialog"]["human_utterances"][-1] + + +def get_bot_utterances(ctx: Context, _) -> list: + return [] if ctx.validation else ctx.misc["agent"]["dialog"]["bot_utterances"] + + +def get_last_bot_utterance(ctx: Context, _) -> dict: + if not ctx.validation and ctx.misc["agent"]["dialog"]["bot_utterances"]: + return ctx.misc["agent"]["dialog"]["bot_utterances"][-1] + else: + return {"text": "", "annotations": {}} + + +def save_to_shared_memory(ctx: Context, _, **kwargs): + if not ctx.validation: + ctx.misc["agent"]["shared_memory"].update(kwargs) + + +def update_used_links(ctx: Context, _, linked_skill_name, linking_phrase): + if not ctx.validation: + agent = ctx.misc["agent"] + agent["used_links"][linked_skill_name] = agent["used_links"].get(linked_skill_name, []) + [linking_phrase] + + +def get_new_link_to(ctx: Context, pipeline: Pipeline, skill_names): + used_links = get_used_links(ctx, pipeline) + disliked_skills = get_disliked_skills(ctx, pipeline) + + link = common_link.link_to( + skill_names, human_attributes={"used_links": used_links, "disliked_skills": disliked_skills} + ) + update_used_links(ctx, pipeline, link["skill"], link["phrase"]) + return link + + +def set_dff_suspension(ctx: Context, _): + if not ctx.validation: + ctx.misc["agent"]["current_turn_dff_suspended"] = True + + +def reset_dff_suspension(ctx: Context, _): + if not ctx.validation: + ctx.misc["agent"]["current_turn_dff_suspended"] = False + + +def set_confidence(ctx: Context, pipeline: Pipeline, confidence=1.0): + if not ctx.validation: + ctx.misc["agent"]["response"].update({"confidence": confidence}) + if confidence == 0.0: + reset_can_continue(ctx, pipeline) + + +def set_can_continue(ctx: Context, _, continue_flag=common_constants.CAN_CONTINUE_SCENARIO): + if not ctx.validation: + ctx.misc["agent"]["response"].update({"can_continue": continue_flag}) + + +def reset_can_continue(ctx: Context, _): + if not ctx.validation and "can_continue" in ctx.misc["agent"]["response"]: + del ctx.misc["agent"]["response"]["can_continue"] + + +def get_named_entities_from_human_utterance(ctx: Context, pipeline: Pipeline): + # ent is a dict! ent = {"text": "London":, "type": "LOC"} + entities = common_utils.get_entities( + get_last_human_utterance(ctx, pipeline), + only_named=True, + with_labels=True, + ) + return entities + + +def get_nounphrases_from_human_utterance(ctx: Context, pipeline: Pipeline): + nps = common_utils.get_entities( + get_last_human_utterance(ctx, pipeline), + only_named=False, + with_labels=False, + ) + return nps + + +def get_fact_random_annotations_from_human_utterance(ctx: Context, pipeline: Pipeline) -> dict: + if not ctx.validation: + return ( + get_last_human_utterance(ctx, pipeline) + .get("annotations", {}) + .get("fact_random", {"facts": [], "response": ""}) + ) + else: + return {"facts": [], "response": ""} + + +def get_fact_for_particular_entity_from_human_utterance(ctx: Context, pipeline: Pipeline, entity) -> list: + fact_random_results = get_fact_random_annotations_from_human_utterance(ctx, pipeline) + facts_for_entity = [] + for fact in fact_random_results.get("facts", []): + is_same_entity = fact.get("entity_substr", "").lower() == entity.lower() + is_sorry = "Sorry, I don't know" in fact.get("fact", "") + if is_same_entity and not is_sorry: + facts_for_entity += [fact["fact"]] + + return facts_for_entity + + +def get_news_about_particular_entity_from_human_utterance(ctx: Context, pipeline: Pipeline, entity) -> dict: + last_uttr = get_last_human_utterance(ctx, pipeline) + last_uttr_entities_news = last_uttr.get("annotations", {}).get("news_api_annotator", []) + curr_news = {} + for news_entity in last_uttr_entities_news: + if news_entity["entity"] == entity: + curr_news = news_entity["news"] + break + if not curr_news: + curr_news = common_news.get_news_about_topic(entity, NEWS_API_ANNOTATOR_URL) + + return curr_news + + +def get_facts_from_fact_retrieval(ctx: Context, pipeline: Pipeline) -> list: + annotations = get_last_human_utterance(ctx, pipeline).get("annotations", {}) + if "fact_retrieval" in annotations: + if isinstance(annotations["fact_retrieval"], dict): + return annotations["fact_retrieval"].get("facts", []) + elif isinstance(annotations["fact_retrieval"], list): + return annotations["fact_retrieval"] + return [] + + +def get_unrepeatable_index_from_rand_seq( + ctx: Context, pipeline: Pipeline, seq_name, seq_max, renew_seq_if_empty=False +) -> int: + """Return a unrepeatable index from RANDOM_SEQUENCE. + RANDOM_SEQUENCE is stored in shared merory by name `seq_name`. + RANDOM_SEQUENCE is shuffled [0..`seq_max`]. + RANDOM_SEQUENCE will be updated after index will get out of RANDOM_SEQUENCE if `renew_seq_if_empty` is True + """ + shared_memory = get_shared_memory(ctx, pipeline) + seq = shared_memory.get(seq_name, random.sample(list(range(seq_max)), seq_max)) + if renew_seq_if_empty or seq: + seq = seq if seq else random.sample(list(range(seq_max)), seq_max) + next_index = seq[-1] if seq else None + save_to_shared_memory(ctx, **{seq_name: seq[:-1]}) + return next_index + + +def get_history(ctx: Context, _): + if not ctx.validation: + return ctx.misc["agent"]["history"] + return {} + + +def get_n_last_state(ctx: Context, pipeline: Pipeline, n) -> str: + last_state = "" + history = list(get_history(ctx, pipeline).items()) + if history: + history_sorted = sorted(history, key=lambda x: x[0]) + if len(history_sorted) >= n: + last_state = history_sorted[-n][1] + return last_state + + +def get_last_state(ctx: Context, pipeline: Pipeline) -> str: + last_state = "" + history = list(get_history(ctx, pipeline).items()) + if history: + history_sorted = sorted(history, key=lambda x: x[0]) + last_state = history_sorted[-1][1] + return last_state diff --git a/common/dff_api_v1/integration/facts_utils.py b/common/dff_api_v1/integration/facts_utils.py new file mode 100644 index 0000000000..29fec8f5dc --- /dev/null +++ b/common/dff_api_v1/integration/facts_utils.py @@ -0,0 +1,316 @@ +import json +import logging +import os +import random +import re +import nltk +import requests +import sentry_sdk +import common.constants as common_constants +import common.dff_api_v1.integration.context as context +from dff.script import Context +from dff.pipeline import Pipeline + +from common.wiki_skill import ( + find_page_title, + find_all_titles, + find_paragraph, + used_types_dict, + delete_hyperlinks, + NEWS_MORE, + QUESTION_TEMPLATES, + QUESTION_TEMPLATES_SHORT, + WIKI_BADLIST, +) +from common.universal_templates import CONTINUE_PATTERN +from common.utils import is_no, is_yes + +nltk.download("punkt") + +sentry_sdk.init(os.getenv("SENTRY_DSN")) +logger = logging.getLogger(__name__) +WIKI_FACTS_URL = os.getenv("WIKI_FACTS_URL") + +with open("/src/common/wikihow_cache.json", "r") as fl: + wikihow_cache = json.load(fl) + +memory = {} + +titles_by_type = {} +for elem in used_types_dict: + types = elem.get("types", []) + titles = elem["titles"] + for tp in types: + titles_by_type[tp] = titles + +titles_by_entity_substr = {} +page_titles_by_entity_substr = {} +for elem in used_types_dict: + entity_substrings = elem.get("entity_substr", []) + titles = elem["titles"] + page_title = elem.get("page_title", "") + for substr in entity_substrings: + titles_by_entity_substr[substr] = titles + if page_title: + page_titles_by_entity_substr[substr] = page_title + +questions_by_entity_substr = {} +for elem in used_types_dict: + entity_substrings = elem.get("entity_substr", []) + question = elem.get("intro_question", "") + if question: + for substr in entity_substrings: + questions_by_entity_substr[substr] = question + +wikihowq_by_substr = {} +for elem in used_types_dict: + entity_substrings = elem.get("entity_substr", []) + wikihow_info = elem.get("wikihow_info", {}) + if wikihow_info: + for substr in entity_substrings: + wikihowq_by_substr[substr] = wikihow_info + + +def get_wikipedia_content(page_title, cache_page_dict=None): + page_content = {} + main_pages = {} + try: + if page_title: + if cache_page_dict and page_title in cache_page_dict: + page_content = cache_page_dict[page_title]["page_content"] + main_pages = cache_page_dict[page_title]["main_pages"] + else: + res = requests.post(WIKI_FACTS_URL, json={"wikipedia_titles": [[page_title]]}, timeout=1.0).json() + if res and res[0]["main_pages"] and res[0]["wikipedia_content"]: + page_content = res[0]["wikipedia_content"][0] + main_pages = res[0]["main_pages"][0] + except Exception as e: + sentry_sdk.capture_exception(e) + logger.exception(e) + + return page_content, main_pages + + +def get_wikihow_content(page_title, cache_page_dict=None): + page_content = {} + try: + if page_title: + res = requests.post(WIKI_FACTS_URL, json={"wikihow_titles": [[page_title]]}, timeout=1.0).json() + if res and res[0]["wikihow_content"]: + page_content = res[0]["wikihow_content"][0] + except Exception as e: + sentry_sdk.capture_exception(e) + logger.exception(e) + + return page_content + + +def get_titles(found_entity_substr, found_entity_types, page_content): + all_titles = find_all_titles([], page_content) + titles_we_use = [] + titles_q = {} + for tp in found_entity_types: + tp_titles = titles_by_type.get(tp, {}) + titles_we_use += list(tp_titles.keys()) + titles_q = {**titles_q, **tp_titles} + substr_titles = titles_by_entity_substr.get(found_entity_substr, {}) + titles_we_use += list(substr_titles.keys()) + titles_q = {**titles_q, **substr_titles} + return titles_q, titles_we_use, all_titles + + +def make_facts_str(paragraphs): + facts_str = "" + mentions_list = [] + mention_pages_list = [] + paragraph = "" + if paragraphs: + paragraph = paragraphs[0] + sentences = nltk.sent_tokenize(paragraph) + sentences_list = [] + cur_len = 0 + max_len = 50 + for sentence in sentences: + sanitized_sentence, mentions, mention_pages = delete_hyperlinks(sentence) + words = nltk.word_tokenize(sanitized_sentence) + if cur_len + len(words) < max_len and not re.findall(WIKI_BADLIST, sanitized_sentence): + sentences_list.append(sanitized_sentence) + cur_len += len(words) + mentions_list += mentions + mention_pages_list += mention_pages + if sentences_list: + facts_str = " ".join(sentences_list) + cur_len = 0 + if sentences and not sentences_list: + sentence = sentences[0] + sanitized_sentence, mentions, mention_pages = delete_hyperlinks(sentence) + sentence_parts = sanitized_sentence.split(", ") + mentions_list += mentions + mention_pages_list += mention_pages + for part in sentence_parts: + words = nltk.word_tokenize(part) + if cur_len + len(words) < max_len and not re.findall(WIKI_BADLIST, part): + sentences_list.append(part) + cur_len += len(words) + facts_str = ", ".join(sentences_list) + if facts_str and not facts_str.endswith("."): + facts_str = f"{facts_str}." + return facts_str, mentions_list, mention_pages_list + + +def preprocess_wikihow_page(article_content): + page_content_list = [] + article_content = list(article_content.items()) + for title_num, (title, paragraphs) in enumerate(article_content): + if title != "intro": + for n, paragraph in enumerate(paragraphs): + facts_str = "" + question = "" + sentences = nltk.sent_tokenize(paragraph) + sentences_list = [] + cur_len = 0 + max_len = 50 + for sentence in sentences: + words = nltk.word_tokenize(sentence) + if cur_len + len(words) < max_len: + sentences_list.append(sentence) + cur_len += len(words) + if sentences_list: + facts_str = " ".join(sentences_list) + else: + cur_len = 0 + sentence_parts = sentences[0].split(", ") + for part in sentence_parts: + words = nltk.word_tokenize(part) + if cur_len + len(words) < max_len: + sentences_list.append(part) + cur_len += len(words) + facts_str = ", ".join(sentences_list) + + if n == len(paragraphs) - 1 and title_num != len(article_content) - 1: + next_title = article_content[title_num + 1][0] + question = f"Would you like to know about {next_title.lower()}?" + elif n != len(paragraphs) - 1: + question = random.choice(NEWS_MORE) + response_dict = {"facts_str": facts_str, "question": question} + response = f"{facts_str} {question}".strip().replace(" ", " ") + if response: + page_content_list.append(response_dict) + return page_content_list + + +def preprocess_wikipedia_page(found_entity_substr, found_entity_types, article_content, predefined_titles=None): + logger.info(f"found_entity_substr {found_entity_substr} found_entity_types {found_entity_types}") + titles_q, titles_we_use, all_titles = get_titles(found_entity_substr, found_entity_types, article_content) + if predefined_titles: + titles_we_use = predefined_titles + logger.info(f"titles_we_use {titles_we_use} all_titles {all_titles}") + facts_list = [] + for n, title in enumerate(titles_we_use): + page_title = find_page_title(all_titles, title) + paragraphs = find_paragraph(article_content, page_title) + logger.info(f"page_title {page_title} paragraphs {paragraphs[:2]}") + count_par = 0 + for num, paragraph in enumerate(paragraphs): + facts_str, *_ = make_facts_str([paragraph]) + if facts_str and facts_str.endswith(".") and len(facts_str.split()) > 4: + facts_list.append((title, facts_str)) + count_par += 1 + if count_par == 2: + break + logger.info(f"facts_list {facts_list[:3]}") + page_content_list = [] + for n, (title, facts_str) in enumerate(facts_list): + if n != len(facts_list) - 1: + next_title = facts_list[n + 1][0] + if next_title != title: + if found_entity_substr.lower() in next_title.lower(): + question_template = random.choice(QUESTION_TEMPLATES_SHORT) + question = question_template.format(next_title) + else: + question_template = random.choice(QUESTION_TEMPLATES) + question = question_template.format(next_title, found_entity_substr) + else: + question = random.choice(NEWS_MORE) + response_dict = {"facts_str": facts_str, "question": question} + response = f"{facts_str} {question}".strip().replace(" ", " ") + if response: + page_content_list.append(response_dict) + else: + page_content_list.append( + {"facts_str": facts_str, "question": f"I was very happy to tell you more about {found_entity_substr}."} + ) + logger.info(f"page_content_list {page_content_list}") + return page_content_list + + +def provide_facts_request(ctx: Context, _): + flag = False + wiki = ctx.misc.get("wiki", {}) + user_uttr: dict = ctx.misc.get("agent", {}).get("dialog", {}).get("human_utterances", [{}])[-1] + isno = is_no(user_uttr) + cur_wiki_page = wiki.get("cur_wiki_page", "") + if cur_wiki_page: + wiki_page_content_list = memory.get("wiki_page_content", []) + used_wiki_page_nums = wiki.get("used_wiki_page_nums", {}).get(cur_wiki_page, []) + logger.info(f"request, used_wiki_page_nums {used_wiki_page_nums}") + logger.info(f"request, wiki_page_content_list {wiki_page_content_list[:3]}") + if len(wiki_page_content_list) > 0 and len(used_wiki_page_nums) < len(wiki_page_content_list) and not isno: + flag = True + return flag + + +def provide_facts_response(ctx: Context, pipeline: Pipeline, page_source, wiki_page): + wiki = ctx.misc.get("wiki", {}) + user_uttr: dict = ctx.misc.get("agent", {}).get("dialog", {}).get("human_utterances", [{}])[-1] + isyes = is_yes(user_uttr) or re.findall(CONTINUE_PATTERN, user_uttr["text"]) + response = "" + cur_wiki_page = wiki.get("cur_wiki_page", "") + if not cur_wiki_page: + wiki["cur_wiki_page"] = wiki_page + if page_source == "wikiHow": + page_content = get_wikihow_content(wiki_page, wikihow_cache) + wiki_page_content_list = preprocess_wikihow_page(page_content) + memory["wiki_page_content"] = wiki_page_content_list + elif page_source == "wikipedia": + page_content = get_wikipedia_content(wiki_page) + wiki_page_content_list = preprocess_wikipedia_page(wiki_page.lower(), [], page_content) + memory["wiki_page_content"] = wiki_page_content_list + logger.info(f"wiki_page {wiki_page}") + + used_wiki_page_nums_dict = wiki.get("used_wiki_page_nums", {}) + used_wiki_page_nums = used_wiki_page_nums_dict.get(wiki_page, []) + wiki_page_content_list = memory.get("wiki_page_content", []) + logger.info(f"response, used_wiki_page_nums {used_wiki_page_nums}") + logger.info(f"response, wiki_page_content_list {wiki_page_content_list[:3]}") + + if wiki_page_content_list: + for num, fact in enumerate(wiki_page_content_list): + if num not in used_wiki_page_nums: + facts_str = fact.get("facts_str", "") + question = fact.get("question", "") + response = f"{facts_str} {question}".strip().replace(" ", " ") + used_wiki_page_nums.append(num) + used_wiki_page_nums_dict[wiki_page] = used_wiki_page_nums + wiki["used_wiki_page_nums"] = used_wiki_page_nums_dict + break + + if len(wiki_page_content_list) == len(used_wiki_page_nums): + if len(wiki_page_content_list) == len(used_wiki_page_nums): + wiki["wiki_page"] = "" + memory["wiki_page_content"] = [] + logger.info(f"response, final {response}") + if response: + if isyes: + context.set_confidence(ctx, pipeline, 1.0) + context.set_can_continue(ctx, pipeline, common_constants.MUST_CONTINUE) + else: + context.set_confidence(ctx, pipeline, 0.99) + context.set_can_continue(ctx, pipeline, common_constants.CAN_CONTINUE_SCENARIO) + else: + context.set_confidence(ctx, pipeline, 0.0) + context.set_can_continue(ctx, pipeline, common_constants.CAN_NOT_CONTINUE) + processed_node = ctx.framework_states["actor"].get("processed_node", ctx.framework_states["actor"]["next_node"]) + processed_node.response = response + ctx.framework_states["actor"]["processed_node"] = processed_node + return ctx diff --git a/common/dff_api_v1/integration/message.py b/common/dff_api_v1/integration/message.py new file mode 100644 index 0000000000..3c65b8bad6 --- /dev/null +++ b/common/dff_api_v1/integration/message.py @@ -0,0 +1,9 @@ +from typing import Optional +from dff.script import Message + + +class DreamMessage(Message): + confidence: Optional[float] = None + human_attr: Optional[dict] = None + bot_attr: Optional[dict] = None + hype_attr: Optional[dict] = None diff --git a/common/dff_api_v1/integration/processing.py b/common/dff_api_v1/integration/processing.py new file mode 100644 index 0000000000..3bff6e3297 --- /dev/null +++ b/common/dff_api_v1/integration/processing.py @@ -0,0 +1,92 @@ +import logging + +from dff.script import Context +from dff.pipeline import Pipeline +import common.constants as common_constants +from common.wiki_skill import extract_entity +from .facts_utils import provide_facts_response +from . import context + + +DREAM_SLOTS_KEY = "slots" +logger = logging.getLogger(__name__) + + +def save_slots_to_ctx(slots: dict): + def save_slots_to_ctx_processing( + ctx: Context, + _, + *args, + **kwargs, + ) -> Context: + ctx.misc[DREAM_SLOTS_KEY] = ctx.misc.get(DREAM_SLOTS_KEY, {}) | slots + return ctx + + return save_slots_to_ctx_processing + + +def entities(**kwargs): + slot_info = list(kwargs.items()) + + def extract_entities( + ctx: Context, + pipeline: Pipeline, + *args, + **kwargs, + ) -> Context: + for slot_name, slot_types in slot_info: + if isinstance(slot_types, str): + extracted_entity = extract_entity(ctx, slot_types) + if extracted_entity: + ctx = save_slots_to_ctx({slot_name: extracted_entity})(ctx, pipeline) + elif isinstance(slot_types, list): + found = False + for slot_type in slot_types: + if not slot_type.startswith("default:"): + extracted_entity = extract_entity(ctx, slot_type) + if extracted_entity: + ctx = save_slots_to_ctx({slot_name: extracted_entity})(ctx, pipeline) + found = True + if not found: + default_value = "" + for slot_type in slot_types: + if slot_type.startswith("default:"): + default_value = slot_type.split("default:")[1] + if default_value: + ctx = save_slots_to_ctx({slot_name: default_value})(ctx, pipeline) + return ctx + + return extract_entities + + +def fact_provider(page_source, wiki_page): + def response(ctx: Context, pipeline: Pipeline, *args, **kwargs): + return provide_facts_response(ctx, pipeline, page_source, wiki_page) + + return response + + +def set_confidence(confidence: float = 1.0): + def set_confidence_processing( + ctx: Context, + pipeline: Pipeline, + *args, + **kwargs, + ) -> Context: + context.set_confidence(ctx, pipeline, confidence) + return ctx + + return set_confidence_processing + + +def set_can_continue(continue_flag: str = common_constants.CAN_CONTINUE_SCENARIO): + def set_can_continue_processing( + ctx: Context, + pipeline: Pipeline, + *args, + **kwargs, + ) -> Context: + context.set_can_continue(ctx, pipeline, continue_flag) + return ctx + + return set_can_continue_processing diff --git a/common/dff_api_v1/integration/response.py b/common/dff_api_v1/integration/response.py new file mode 100644 index 0000000000..e43a78b5f6 --- /dev/null +++ b/common/dff_api_v1/integration/response.py @@ -0,0 +1,19 @@ +from dff.script import Context +from .processing import DREAM_SLOTS_KEY +from .message import DreamMessage + + +def fill_by_slots(response: DreamMessage): + def fill_responses_by_slots_inner( + ctx: Context, + _, + *args, + **kwargs, + ) -> Context: + if not response.text: + return response + for slot_name, slot_value in ctx.misc.get(DREAM_SLOTS_KEY, {}).items(): + response.text = response.text.replace("{" f"{slot_name}" "}", slot_value) + return response + + return fill_responses_by_slots_inner diff --git a/common/dff_api_v1/integration/serializer.py b/common/dff_api_v1/integration/serializer.py new file mode 100644 index 0000000000..bc4f4e5dce --- /dev/null +++ b/common/dff_api_v1/integration/serializer.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python + +import logging +import os +from typing import List, Any + +import sentry_sdk +from sentry_sdk.integrations.logging import ignore_logger +from dff.script import Context, MultiMessage +from dff.pipeline import Pipeline +from pydantic import BaseModel, Field, Extra, root_validator + +from common.constants import CAN_NOT_CONTINUE, CAN_CONTINUE_SCENARIO +from common.dff_api_v1.integration.context import get_last_human_utterance +from common.dff_api_v1.integration.message import DreamMessage + + +ignore_logger("root") + +sentry_sdk.init(os.getenv("SENTRY_DSN")) +SERVICE_NAME = os.getenv("SERVICE_NAME") + +logger = logging.getLogger(__name__) + + +class ExtraIgnoreModel(BaseModel): + class Config: + extra = Extra.ignore + + +class HumanAttr(ExtraIgnoreModel): + dff_shared_state: dict + used_links: dict + age_group: str + disliked_skills: list + + +class HypeAttr(ExtraIgnoreModel): + can_continue: str + + @root_validator(pre=True) + def calculate_can_continue(cls, values): + confidence = values["response"].get("confidence", 0.85) + can_continue = CAN_CONTINUE_SCENARIO if confidence else CAN_NOT_CONTINUE + values["can_continue"] = values["response"].get("can_continue", can_continue) + return values + + +class State(ExtraIgnoreModel): + context: Context + previous_human_utter_index: int = -1 + current_turn_dff_suspended: bool = False + history: dict = Field(default_factory=dict) + shared_memory: dict = Field(default_factory=dict) + + @root_validator(pre=True) + def shift_state_history(cls, values): + values["previous_human_utter_index"] = values["human_utter_index"] + values["history"][str(values["human_utter_index"])] = list(values["context"].labels.values())[-1] + return values + + @root_validator(pre=True) + def validate_context(cls, values): + context = values["context"] + context.clear(2, ["requests", "responses", "labels"]) + del context.misc["agent"] + return values + + +class Agent(ExtraIgnoreModel): + previous_human_utter_index: int = -1 + human_utter_index: int + dialog: Any + entities: dict = Field(default_factory=dict) + shared_memory: dict = Field(default_factory=dict) + current_turn_dff_suspended: bool = False + previous_turn_dff_suspended: bool = False + response: dict = Field(default_factory=dict) + dff_shared_state: dict = Field(default_factory=dict) + cache: dict = Field(default_factory=dict) + history: dict = Field(default_factory=dict) + used_links: dict = Field(default_factory=dict) + age_group: str = "" + disliked_skills: list = Field(default_factory=list) + clarification_request_flag: bool = False + + @root_validator(pre=True) + def get_state_props(cls, values): + state = values.get("state", {}) + values = values | state + return values + + +def load_ctxs(requested_data) -> List[Context]: + dialog_batch = requested_data.get("dialog_batch", []) + human_utter_index_batch = requested_data.get("human_utter_index_batch", [0] * len(dialog_batch)) + state_batch = requested_data.get(f"{SERVICE_NAME}_state_batch", [{}] * len(dialog_batch)) + dff_shared_state_batch = requested_data.get("dff_shared_state_batch", [{}] * len(dialog_batch)) + entities_batch = requested_data.get("entities_batch", [{}] * len(dialog_batch)) + used_links_batch = requested_data.get("used_links_batch", [{}] * len(dialog_batch)) + age_group_batch = requested_data.get("age_group_batch", [""] * len(dialog_batch)) + disliked_skills_batch = requested_data.get("disliked_skills_batch", [{}] * len(dialog_batch)) + clarification_request_flag_batch = requested_data.get( + "clarification_request_flag_batch", + [False] * len(dialog_batch), + ) + ctxs = [] + for ( + human_utter_index, + dialog, + state, + dff_shared_state, + entities, + used_links, + age_group, + disliked_skills, + clarification_request_flag, + ) in zip( + human_utter_index_batch, + dialog_batch, + state_batch, + dff_shared_state_batch, + entities_batch, + used_links_batch, + age_group_batch, + disliked_skills_batch, + clarification_request_flag_batch, + ): + ctx = Context.cast(state.get("context", {})) + agent = Agent( + human_utter_index=human_utter_index, + dialog=dialog, + state=state, + dff_shared_state=dff_shared_state, + entities=entities, + used_links=used_links, + age_group=age_group, + disliked_skills=disliked_skills, + clarification_request_flag=clarification_request_flag, + ) + ctx.misc["agent"] = agent.dict() + ctxs += [ctx] + return ctxs + + +def get_response(ctx: Context, _): + agent = ctx.misc["agent"] + response_parts = agent.get("response_parts", []) + confidence = agent["response"].get("confidence", 0.85) + state = State(context=ctx, **agent).dict(exclude_none=True) + human_attr = HumanAttr.parse_obj(agent).dict() | {f"{SERVICE_NAME}_state": state} + hype_attr = HypeAttr.parse_obj(agent).dict() | ({"response_parts": response_parts} if response_parts else {}) + response = ctx.last_response + if isinstance(response, MultiMessage): + responses = [] + message: dict + for message in response.messages: + reply = message.text or "" + conf = message.confidence or confidence + h_a = human_attr | (message.human_attr or {}) + attr = hype_attr | (message.hype_attr or {}) + b_a = message.bot_attr or {} + responses += [(reply, conf, h_a, b_a, attr)] + return list(zip(*responses)) + else: + return (response.text, confidence, human_attr, {}, hype_attr) + + +def run_dff(ctx: Context, pipeline: Pipeline): + last_request = get_last_human_utterance(ctx, pipeline.actor)["text"] + pipeline.context_storage[ctx.id] = ctx + ctx = pipeline(DreamMessage(text=last_request), ctx.id) + response = get_response(ctx, pipeline.actor) + del pipeline.context_storage[ctx.id] + return response diff --git a/common/dff_api_v1/requirements.txt b/common/dff_api_v1/requirements.txt new file mode 100644 index 0000000000..36c99b7133 --- /dev/null +++ b/common/dff_api_v1/requirements.txt @@ -0,0 +1,13 @@ +# base +six==1.16.0 +sentry_sdk[flask]==0.14.3 +flask==1.1.1 +itsdangerous==2.0.1 +gunicorn==19.9.0 +healthcheck==1.3.3 +# bound to a specific commit temporarily +dff==0.4.1 +# test +requests==2.22.0 +jinja2<=3.0.3 +Werkzeug<=2.0.3 diff --git a/skills/dff_science_skill/tests/link_to_in.json b/skills/dff_science_skill/tests/link_to_in.json index fc68e4ddda..e37a6f880e 100644 --- a/skills/dff_science_skill/tests/link_to_in.json +++ b/skills/dff_science_skill/tests/link_to_in.json @@ -550,6 +550,9 @@ "disliked_skills_batch": [ [] ], + "prompts_goals_batch": [ + {} + ], "clarification_request_flag_batch": [ false ], diff --git a/skills/dff_template_skill/Dockerfile b/skills/dff_template_skill/Dockerfile index ecf5468f99..ec00d93de2 100644 --- a/skills/dff_template_skill/Dockerfile +++ b/skills/dff_template_skill/Dockerfile @@ -3,7 +3,7 @@ FROM python:3.9.16-slim # Do not change anything in this section WORKDIR /src -COPY common/dff/requirements.txt . +COPY common/dff_api_v1/requirements.txt . RUN pip install -r requirements.txt # ###################### CUSTOM SECTION ###################################### diff --git a/skills/dff_template_skill/scenario/condition.py b/skills/dff_template_skill/scenario/condition.py index 7ef3b07924..2c266b6abd 100644 --- a/skills/dff_template_skill/scenario/condition.py +++ b/skills/dff_template_skill/scenario/condition.py @@ -1,15 +1,16 @@ import logging -from df_engine.core import Context, Actor +from dff.script import Context +from dff.pipeline import Pipeline -from common.dff.integration import condition as int_cnd +from common.dff_api_v1.integration import condition as int_cnd logger = logging.getLogger(__name__) # .... def example_lets_talk_about(): - def example_lets_talk_about_handler(ctx: Context, actor: Actor, *args, **kwargs) -> str: - return int_cnd.is_lets_chat_about_topic_human_initiative(ctx, actor) + def example_lets_talk_about_handler(ctx: Context, pipeline: Pipeline) -> str: + return int_cnd.is_lets_chat_about_topic_human_initiative(ctx, pipeline) return example_lets_talk_about_handler diff --git a/skills/dff_template_skill/scenario/main.py b/skills/dff_template_skill/scenario/main.py index 8be7bc6ec7..c831ad4b22 100644 --- a/skills/dff_template_skill/scenario/main.py +++ b/skills/dff_template_skill/scenario/main.py @@ -1,14 +1,24 @@ import logging import re -from df_engine.core.keywords import LOCAL, PROCESSING, TRANSITIONS, RESPONSE, GLOBAL -from df_engine.core import Actor -import df_engine.conditions as cnd -import df_engine.labels as lbl -import common.dff.integration.condition as int_cnd -import common.dff.integration.processing as int_prs -import common.dff.integration.response as int_rsp +import dff.script.conditions as cnd +import dff.script.labels as lbl +from dff.script import ( + MultiMessage, + LOCAL, + PRE_TRANSITIONS_PROCESSING, + PRE_RESPONSE_PROCESSING, + TRANSITIONS, + RESPONSE, + GLOBAL, +) +from dff.pipeline import Pipeline + +import common.dff_api_v1.integration.condition as int_cnd +import common.dff_api_v1.integration.processing as int_prs +import common.dff_api_v1.integration.response as int_rsp +from common.dff_api_v1.integration.message import DreamMessage import common.constants as common_constants @@ -19,36 +29,38 @@ logger = logging.getLogger(__name__) # First of all, to create a dialog agent, we need to create a dialog script. -# Below, `flows` is the dialog script. -# A dialog script is a flow dictionary that can contain multiple flows . -# Flows are needed in order to divide a dialog into sub-dialogs and process them separately. +# Below, we lay out our script and assign it to the `script` variable. +# A dialog script is a dictionary each item of which corresponds to a different namespace, aka flow. +# Flows allow you to divide a dialog into sub-dialogs and process them separately. # For example, the separation can be tied to the topic of the dialog. -# In our example, there is one flow called greeting_flow. - -# Inside each flow, we can describe a sub-dialog using keyword `GRAPH` from df_engine.core.keywords module. -# Here we can also use keyword `GLOBAL_TRANSITIONS`, which we have considered in other examples. +# In our example, there is one flow called greeting. -# `GRAPH` describes a sub-dialog using linked nodes, each node has the keywords `RESPONSE` and `TRANSITIONS`. +# A flow describes a sub-dialog using linked nodes, each node has the keywords `RESPONSE` and `TRANSITIONS`. -# `RESPONSE` - contains the response that the dialog agent will return when transitioning to this node. +# `RESPONSE` - contains the response that the dialog agent will return when visiting this node. # `TRANSITIONS` - describes transitions from the current node to other nodes. # `TRANSITIONS` are described in pairs: -# - the node to which the agent will perform the transition +# - the node to visit # - the condition under which to make the transition -flows = { +script = { GLOBAL: { TRANSITIONS: { ("greeting", "node1"): cnd.regexp(r"\bhi\b"), }, }, - "sevice": { + "service": { "start": { - RESPONSE: "", + RESPONSE: DreamMessage(text=""), + # We simulate extraction of two slots at the dialog start. + # Slot values are then used in the dialog. + PRE_TRANSITIONS_PROCESSING: { + "save_slots_to_ctx": int_prs.save_slots_to_ctx({"topic": "science", "user_name": "Gordon Freeman"}) + }, TRANSITIONS: {("greeting", "node1"): cnd.true()}, }, "fallback": { - RESPONSE: "Ooops", + RESPONSE: DreamMessage(text="Ooops"), TRANSITIONS: { lbl.previous(): cnd.regexp(r"previous", re.IGNORECASE), lbl.repeat(0.2): cnd.true(), @@ -57,62 +69,71 @@ }, "greeting": { LOCAL: { - PROCESSING: { + PRE_RESPONSE_PROCESSING: { "set_confidence": int_prs.set_confidence(1.0), "set_can_continue": int_prs.set_can_continue(), - # "fill_responses_by_slots": int_prs.fill_responses_by_slots(), }, }, "node1": { - RESPONSE: int_rsp.multi_response(replies=["Hi, how are you?", "Hi, what's up?"]), # several hypothesis - PROCESSING: { - "save_slots_to_ctx": int_prs.save_slots_to_ctx({"topic": "science", "user_name": "Gordon Freeman"}) - }, + RESPONSE: MultiMessage( + messages=[DreamMessage(text="Hi, how are you?"), DreamMessage(text="Hi, what's up?")] + ), # several hypotheses TRANSITIONS: {"node2": cnd.regexp(r"how are you", re.IGNORECASE)}, }, "node2": { - RESPONSE: loc_rsp.example_response("Good. What do you want to talk about?"), - # loc_rsp.example_response is just for an example, you can use just str without example_response func + # The response section can contain any function that returns a DreamMessage. + RESPONSE: loc_rsp.example_response(DreamMessage(text="Good. What do you want to talk about?")), TRANSITIONS: {"node3": loc_cnd.example_lets_talk_about()}, }, "node3": { - RESPONSE: "Sorry, I can not talk about that now. Maybe late. Do you like {topic}?", - PROCESSING: {"fill_responses_by_slots": int_prs.fill_responses_by_slots()}, + RESPONSE: int_rsp.fill_by_slots( + DreamMessage(text="Sorry, I can not talk about that now. Maybe later. Do you like {topic}?") + ), TRANSITIONS: { "node4": int_cnd.is_yes_vars, "node5": int_cnd.is_no_vars, "node6": int_cnd.is_do_not_know_vars, - "node7": cnd.true(), # it will be chosen if other conditions are False + "node7": cnd.true(), # this option will be chosen if no other condition is met. }, }, "node4": { - RESPONSE: "I like {topic} too, {user_name}", - PROCESSING: {"fill_responses_by_slots": int_prs.fill_responses_by_slots()}, + # Invoke a special function to insert slots to the response. + RESPONSE: int_rsp.fill_by_slots(DreamMessage(text="I like {topic} too, {user_name}")), TRANSITIONS: {("node7", 0.1): cnd.true()}, }, "node5": { - RESPONSE: "I do not like {topic} too, {user_name}", - PROCESSING: {"fill_responses_by_slots": int_prs.fill_responses_by_slots()}, + RESPONSE: int_rsp.fill_by_slots(DreamMessage(text="I do not like {topic} too, {user_name}")), TRANSITIONS: {("node7", 0.1): cnd.true()}, }, "node6": { - RESPONSE: "I have no opinion about {topic} too, {user_name}", - PROCESSING: {"fill_responses_by_slots": int_prs.fill_responses_by_slots()}, + RESPONSE: int_rsp.fill_by_slots(DreamMessage(text="I have no opinion about {topic} too, {user_name}")), TRANSITIONS: {("node7", 0.1): cnd.true()}, }, "node7": { - RESPONSE: int_rsp.multi_response( - replies=["bye", "goodbye"], - confidences=[1.0, 0.5], - hype_attr=[ - {"can_continue": common_constants.MUST_CONTINUE}, # for the first hyp - {"can_continue": common_constants.CAN_CONTINUE_SCENARIO}, # for the second hyp - ], + RESPONSE: MultiMessage( + # DreamMessage attributes, like confidence and can_continue status + # can be used to override the parameters extracted by Dream engine. + messages=[ + DreamMessage( + text="bye", confidence=1.0, hype_attr={"can_continue": common_constants.MUST_CONTINUE} + ), + DreamMessage( + text="goodbye", + confidence=0.5, + hype_attr={"can_continue": common_constants.CAN_CONTINUE_SCENARIO}, + ), + ] ), - PROCESSING: {"set_confidence": int_prs.set_confidence(0.0)}, + PRE_RESPONSE_PROCESSING: {"set_confidence": int_prs.set_confidence(0.0)}, }, }, } -actor = Actor(flows, start_label=("sevice", "start"), fallback_label=("sevice", "fallback")) +db = dict() +pipeline = Pipeline.from_script( + script=script, + start_label=("service", "start"), + fallback_label=("service", "fallback"), + context_storage=db, +) diff --git a/skills/dff_template_skill/scenario/response.py b/skills/dff_template_skill/scenario/response.py index 50a849c902..7c2551a5e8 100644 --- a/skills/dff_template_skill/scenario/response.py +++ b/skills/dff_template_skill/scenario/response.py @@ -1,6 +1,6 @@ import logging -from df_engine.core import Context, Actor +from dff.script import Context logger = logging.getLogger(__name__) @@ -8,7 +8,7 @@ def example_response(reply: str): - def example_response_handler(ctx: Context, actor: Actor, *args, **kwargs) -> str: + def example_response_handler(ctx: Context, _) -> str: return reply return example_response_handler diff --git a/skills/dff_template_skill/server.py b/skills/dff_template_skill/server.py index 72e91e68c5..8c7fbddad7 100644 --- a/skills/dff_template_skill/server.py +++ b/skills/dff_template_skill/server.py @@ -4,16 +4,14 @@ import time import os import random +from common.dff_api_v1.integration.serializer import load_ctxs, run_dff from flask import Flask, request, jsonify from healthcheck import HealthCheck import sentry_sdk from sentry_sdk.integrations.logging import ignore_logger - -from common.dff.integration.actor import load_ctxs, get_response - -from scenario.main import actor +from scenario.main import pipeline # import test_server @@ -45,8 +43,7 @@ def handler(requested_data, random_seed=None): # for tests if random_seed: random.seed(int(random_seed)) - ctx = actor(ctx) - responses.append(get_response(ctx, actor)) + responses.append(run_dff(ctx, pipeline)) except Exception as exc: sentry_sdk.capture_exception(exc) logger.exception(exc) diff --git a/skills/dff_template_skill/tests/lets_talk_out.json b/skills/dff_template_skill/tests/lets_talk_out.json index 229271fb1e..83ff55d7d0 100644 --- a/skills/dff_template_skill/tests/lets_talk_out.json +++ b/skills/dff_template_skill/tests/lets_talk_out.json @@ -29,25 +29,21 @@ ] }, "requests": { - "0": "hi." + "0": { + "text": "hi." + } }, "responses": { - "0": [ - [ - "Hi, how are you?", - 0.0, - {}, - {}, - {} - ], - [ - "Hi, what's up?", - 0.0, - {}, - {}, - {} + "0": { + "messages": [ + { + "text": "Hi, how are you?" + }, + { + "text": "Hi, what's up?" + } ] - ] + } }, "misc": { "slots": { @@ -56,7 +52,7 @@ } }, "validation": false, - "actor_state": {} + "framework_states": {} } }, "dff_shared_state": { @@ -88,25 +84,21 @@ ] }, "requests": { - "0": "hi." + "0": { + "text": "hi." + } }, "responses": { - "0": [ - [ - "Hi, how are you?", - 0.0, - {}, - {}, - {} - ], - [ - "Hi, what's up?", - 0.0, - {}, - {}, - {} + "0": { + "messages": [ + { + "text": "Hi, how are you?" + }, + { + "text": "Hi, what's up?" + } ] - ] + } }, "misc": { "slots": { @@ -115,7 +107,7 @@ } }, "validation": false, - "actor_state": {} + "framework_states": {} } }, "dff_shared_state": { From 407b48f075913e8d45ba764a0d07bd4b1a8e0127 Mon Sep 17 00:00:00 2001 From: dmitrijeuseew Date: Thu, 22 Jun 2023 11:21:38 +0300 Subject: [PATCH 04/11] Filter entities by offsets (#497) * Fix requirements.txt (#84) * fix itsdangerous requirements * pin itsdangerous requirements for all flask==1.1.1 servers * fix entity offsets * codestyle --------- Co-authored-by: Andrii.Hura <54397922+AndriiHura@users.noreply.github.com> Co-authored-by: mtalimanchuk Co-authored-by: Dilyara Baymurzina --- annotators/entity_detection/server.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/annotators/entity_detection/server.py b/annotators/entity_detection/server.py index ffd6262b6c..18b3babcea 100644 --- a/annotators/entity_detection/server.py +++ b/annotators/entity_detection/server.py @@ -70,6 +70,7 @@ def get_result(request, what_to_annotate): utts_list = [] utts_list_init = [] utts_nums = [] + uttr_offsets = [] for n, hist_utt in enumerate(last_utts): if isinstance(hist_utt, str): hist_utt = [hist_utt] @@ -81,12 +82,15 @@ def get_result(request, what_to_annotate): prev_utt = hist_utt[-2] if prev_utt and prev_utt[-1] not in {".", "!", "?"}: prev_utt = f"{prev_utt}." + offset = len(prev_utt) concat_utt = f"{prev_utt} {last_utt}" else: + offset = 0 concat_utt = last_utt utts_list.append(concat_utt) utts_list_init.append(concat_utt) utts_nums.append(n) + uttr_offsets.append(offset) utt_entities_batch = [{} for _ in last_utts] utt_entities = {} @@ -110,6 +114,7 @@ def get_result(request, what_to_annotate): entity_offsets_list, uttr, num, + uttr_offset, ) in zip( entity_substr_batch, tags_batch, @@ -117,6 +122,7 @@ def get_result(request, what_to_annotate): entity_offsets_batch, utts_list_init, utts_nums, + uttr_offsets, ): utt_entities = {} for entity, tag, proba, (start_offset, end_offset) in zip( @@ -129,7 +135,10 @@ def get_result(request, what_to_annotate): entity.lower() not in stopwords and len(entity) > 2 and not (len(entity.split()) == 1 and nlp(entity)[0].pos_ == "PRON") + and start_offset >= uttr_offset ): + start_offset -= uttr_offset + end_offset -= uttr_offset entity = EVERYTHING_EXCEPT_LETTERS_DIGITALS_AND_SPACE.sub(" ", entity) entity = DOUBLE_SPACES.sub(" ", entity).strip() finegrained_tag = replace_tag_dict.get(tag.lower(), tag.lower()) From fd8c7ef8e563c77a347439788da0ad3cbedc9579 Mon Sep 17 00:00:00 2001 From: dmitrijeuseew Date: Mon, 26 Jun 2023 19:43:06 +0300 Subject: [PATCH 05/11] custom entity linking (#335) * custom entity linking * codestyle * update * refactor * update pipeline_conf * fix: removed extra services, added property-extraction to pipeline * fix proxy * update custom_el * chore: Add component & container cards * style: optimize Dockerfile for custom_el * style: flake and black up * style: flake and black up properly * fix: source for property extraction, proxy * fix: Download stopwords in Dockerfile * added dff-persona-prompted-skill * fix: dist name * refactor: Split respond function into many * style: flake and black up * fix: Add default values in case of failure --------- Co-authored-by: kpetyxova Co-authored-by: Ramimashkouk Co-authored-by: Rami <54779216+Ramimashkouk@users.noreply.github.com> Co-authored-by: dilyararimovna --- annotators/custom_entity_linking/Dockerfile | 42 ++ .../custom_entity_linking/abstract_rels.txt | 11 + .../custom_entity_linking.json | 34 ++ .../custom_entity_linking/requirements.txt | 11 + annotators/custom_entity_linking/server.py | 216 +++++++ .../custom-entity-linking/environment.yml | 5 + .../custom-entity-linking/service.yml | 26 + .../src/entity_linking.py | 263 +++++++++ annotators/custom_entity_linking/test.sh | 3 + annotators/custom_entity_linking/test_el.py | 48 ++ assistant_dists/dream_kg/cpu.yml | 26 + assistant_dists/dream_kg/db_conf.json | 6 + assistant_dists/dream_kg/dev.yml | 107 ++++ .../dream_kg/docker-compose.override.yml | 314 ++++++++++ assistant_dists/dream_kg/gpu1.yml | 58 ++ assistant_dists/dream_kg/pipeline_conf.json | 536 ++++++++++++++++++ assistant_dists/dream_kg/proxy.yml | 118 ++++ assistant_dists/dream_kg/telegram.yml | 17 + assistant_dists/dream_kg/test.yml | 46 ++ components.tsv | 2 +- components/7v330b9odypuiugqeiuqij.yml | 26 + state_formatters/dp_formatters.py | 29 +- 22 files changed, 1933 insertions(+), 11 deletions(-) create mode 100644 annotators/custom_entity_linking/Dockerfile create mode 100644 annotators/custom_entity_linking/abstract_rels.txt create mode 100644 annotators/custom_entity_linking/custom_entity_linking.json create mode 100644 annotators/custom_entity_linking/requirements.txt create mode 100644 annotators/custom_entity_linking/server.py create mode 100644 annotators/custom_entity_linking/service_configs/custom-entity-linking/environment.yml create mode 100644 annotators/custom_entity_linking/service_configs/custom-entity-linking/service.yml create mode 100644 annotators/custom_entity_linking/src/entity_linking.py create mode 100755 annotators/custom_entity_linking/test.sh create mode 100644 annotators/custom_entity_linking/test_el.py create mode 100644 assistant_dists/dream_kg/cpu.yml create mode 100644 assistant_dists/dream_kg/db_conf.json create mode 100644 assistant_dists/dream_kg/dev.yml create mode 100644 assistant_dists/dream_kg/docker-compose.override.yml create mode 100644 assistant_dists/dream_kg/gpu1.yml create mode 100644 assistant_dists/dream_kg/pipeline_conf.json create mode 100644 assistant_dists/dream_kg/proxy.yml create mode 100644 assistant_dists/dream_kg/telegram.yml create mode 100644 assistant_dists/dream_kg/test.yml create mode 100644 components/7v330b9odypuiugqeiuqij.yml diff --git a/annotators/custom_entity_linking/Dockerfile b/annotators/custom_entity_linking/Dockerfile new file mode 100644 index 0000000000..755f0e473c --- /dev/null +++ b/annotators/custom_entity_linking/Dockerfile @@ -0,0 +1,42 @@ +FROM tensorflow/tensorflow:1.15.2-gpu + +WORKDIR /src + +RUN apt-key del 7fa2af80 && \ + rm -f /etc/apt/sources.list.d/cuda*.list && \ + curl https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-keyring_1.0-1_all.deb \ + -o cuda-keyring_1.0-1_all.deb && \ + dpkg -i cuda-keyring_1.0-1_all.deb +RUN apt-get -y update +RUN apt-get install -y build-essential zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget llvm \ + libncurses5-dev libncursesw5-dev xz-utils libffi-dev liblzma-dev + +RUN apt-get -y update && \ + apt-get install -y software-properties-common && \ + apt-get update && apt-get install git -y + +RUN apt-get install -y sqlite3 + +ARG LANGUAGE=EN +ENV LANGUAGE ${LANGUAGE} + +ARG CONFIG +ARG PORT +ARG SRC_DIR +ARG SED_ARG=" | " + +ENV CONFIG=$CONFIG +ENV PORT=$PORT + +COPY ./annotators/custom_entity_linking/requirements.txt ./requirements.txt +RUN pip install -r ./requirements.txt + +COPY $SRC_DIR . + + +RUN python -m deeppavlov install $CONFIG && \ + python -c "import nltk; nltk.download('stopwords')" + +RUN sed -i "s|$SED_ARG|g" "$CONFIG" + +CMD gunicorn --workers=1 --timeout 500 server:app -b 0.0.0.0:${PORT} diff --git a/annotators/custom_entity_linking/abstract_rels.txt b/annotators/custom_entity_linking/abstract_rels.txt new file mode 100644 index 0000000000..318592a414 --- /dev/null +++ b/annotators/custom_entity_linking/abstract_rels.txt @@ -0,0 +1,11 @@ +favorite animal +like animal +favorite book +like read +favorite movie +favorite food +like food +favorite drink +like drink +favorite sport +like sports diff --git a/annotators/custom_entity_linking/custom_entity_linking.json b/annotators/custom_entity_linking/custom_entity_linking.json new file mode 100644 index 0000000000..d192634536 --- /dev/null +++ b/annotators/custom_entity_linking/custom_entity_linking.json @@ -0,0 +1,34 @@ +{ + "chainer": { + "in": ["user_ids", "entity_substr", "entity_tags"], + "pipe": [ + { + "class_name": "src.entity_linking:EntityLinker", + "in": ["user_ids", "entity_substr", "entity_tags"], + "out": ["entity_ids", "entity_conf", "entity_id_tags"], + "load_path": "{DOWNLOADS_PATH}/entity_linking_eng/custom_el_eng_dream", + "rank_in_runtime": true, + "num_entities_for_bert_ranking": 20, + "use_gpu": false, + "include_mention": false, + "num_entities_to_return": 5, + "lemmatize": true, + "use_tags": true, + "use_descriptions": true, + "full_paragraph": true, + "return_confidences": true, + "lang": "en" + } + ], + "out": ["entity_substr", "entity_ids", "entity_conf", "entity_id_tags"] + }, + "metadata": { + "variables": { + "ROOT_PATH": "~/.deeppavlov", + "DOWNLOADS_PATH": "{ROOT_PATH}/downloads", + "MODELS_PATH": "{ROOT_PATH}/models", + "TRANSFORMER": "{DOWNLOADS_PATH}/torch_bert_models/bert_small", + "CONFIGS_PATH": "{DEEPPAVLOV_PATH}/configs" + } + } +} diff --git a/annotators/custom_entity_linking/requirements.txt b/annotators/custom_entity_linking/requirements.txt new file mode 100644 index 0000000000..293752dbf6 --- /dev/null +++ b/annotators/custom_entity_linking/requirements.txt @@ -0,0 +1,11 @@ +Flask==1.1.1 +nltk==3.2.5 +gunicorn==19.9.0 +requests==2.22.0 +sentry-sdk==0.12.3 +rapidfuzz==0.7.6 +deeppavlov==0.17.2 +itsdangerous==2.0.1 +jinja2<=3.0.3 +Werkzeug<=2.0.3 +cryptography==2.8 diff --git a/annotators/custom_entity_linking/server.py b/annotators/custom_entity_linking/server.py new file mode 100644 index 0000000000..d6b6091cca --- /dev/null +++ b/annotators/custom_entity_linking/server.py @@ -0,0 +1,216 @@ +import logging +import os +import time +from flask import Flask, request, jsonify +import sentry_sdk +from deeppavlov import build_model + +logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO) +logger = logging.getLogger(__name__) +sentry_sdk.init(os.getenv("SENTRY_DSN")) + +app = Flask(__name__) + +config_name = os.getenv("CONFIG") + +with open("abstract_rels.txt", "r") as inp: + abstract_rels = [line.strip() for line in inp.readlines()] + +try: + el = build_model(config_name, download=True) + logger.info("model loaded") +except Exception as e: + sentry_sdk.capture_exception(e) + logger.exception(e) + raise e + + +@app.route("/add_entities", methods=["POST"]) +def add_entities(): + user_id = request.json.get("user_id", "") + entity_info = request.json.get("entity_info", {}) + entity_substr_list = entity_info.get("entity_substr", []) + entity_ids_list = entity_info.get("entity_ids", []) + tags_list = entity_info.get("tags", []) + el[0].add_custom_entities(user_id, entity_substr_list, entity_ids_list, tags_list) + logger.info(f"added entities {entity_info}") + return {} + + +def preprocess_context(context_batch): + """Preprocesses the context batch by combining previous and current utterances. + + Args: + context_batch (list): List of conversation contexts. + + Returns: + list: Preprocessed context batch. + """ + optimized_context_batch = [] + for hist_uttr in context_batch: + if len(hist_uttr) == 1: + optimized_context_batch.append(hist_uttr[0]) + else: + prev_uttr = hist_uttr[-2] + cur_uttr = hist_uttr[-1] + is_q = ( + any([prev_uttr.startswith(q_word) for q_word in ["what ", "who ", "when ", "where "]]) + or "?" in prev_uttr + ) + if is_q and len(cur_uttr.split()) < 3: + optimized_context_batch.append(f"{prev_uttr} {cur_uttr}") + else: + optimized_context_batch.append(cur_uttr) + + return optimized_context_batch + + +def process_entity_info( + entity_substr_batch, entity_ids_batch, conf_batch, entity_id_tags_batch, prex_info_batch, optimized_context_batch +): + """Processes entity information based on various conditions. + + Args: + entity_substr_batch (list): List of entity substrings (entity names). + entity_ids_batch (list): List of entity IDs. + conf_batch (list): List of confidences. + entity_id_tags_batch (list): List of entity ID tags (entity kinds). + prex_info_batch (list): List of property extraction information. + optimized_context_batch (list): List of preprocessed conversation contexts. + + Returns: + list: Processed entity information batch. + """ + entity_info_batch = [] + for ( + entity_substr_list, + entity_ids_list, + conf_list, + entity_id_tags_list, + prex_info, + context, + ) in zip( + entity_substr_batch, + entity_ids_batch, + conf_batch, + entity_id_tags_batch, + prex_info_batch, + optimized_context_batch, + ): + entity_info_list = [] + triplets = {} + + # Extract triplets from property extraction information + if isinstance(prex_info, list) and prex_info: + prex_info = prex_info[0] + if prex_info: + triplets = prex_info.get("triplets", {}) + + obj2rel_dict = {} + for triplet in triplets: + obj = triplet["object"].lower() + + # Determine the relationship type (relation or property) + if "relation" in triplet: + rel = triplet["relation"] + elif "property" in triplet: + rel = triplet["property"] + else: + rel = "" + obj2rel_dict[obj] = rel + + # Process entity information for each entity substring + for entity_substr, entity_ids, confs, entity_id_tags in zip( + entity_substr_list, + entity_ids_list, + conf_list, + entity_id_tags_list, + ): + entity_info = {} + entity_substr = entity_substr.lower() + context = context.lower() + curr_rel = obj2rel_dict.get(entity_substr, "") + is_abstract = curr_rel.lower().replace("_", " ") in abstract_rels and not any( + [f" {word} {entity_substr}" in context for word in ["the", "my", "his", "her"]] + ) + + filtered_entity_ids, filtered_confs, filtered_entity_id_tags = [], [], [] + + # Filter entity information based on condition: + # - Exclude entities marked as "Abstract" in db if they are not considered + # abstract according to is_abstract. + for entity_id, conf, entity_id_tag in zip(entity_ids, confs, entity_id_tags): + if entity_id_tag.startswith("Abstract") and not is_abstract: + pass + else: + filtered_entity_ids.append(entity_id) + filtered_confs.append(conf) + filtered_entity_id_tags.append(entity_id_tag) + + if filtered_entity_ids and entity_substr in context: + # Construct the entity information dictionary + entity_info["entity_substr"] = entity_substr + entity_info["entity_ids"] = filtered_entity_ids + entity_info["confidences"] = [float(elem[2]) for elem in filtered_confs] + entity_info["tokens_match_conf"] = [float(elem[0]) for elem in filtered_confs] + entity_info["entity_id_tags"] = filtered_entity_id_tags + entity_info_list.append(entity_info) + # Add the processed entity information to the batch + entity_info_batch.append(entity_info_list) + return entity_info_batch + + +@app.route("/model", methods=["POST"]) +def respond(): + """Main function for responding to a request. + + Returns: + flask.Response: Response containing the processed entity information. + """ + st_time = time.time() + user_ids = request.json.get("user_id", [""]) + entity_substr_batch = request.json.get("entity_substr", [[""]]) + entity_tags_batch = request.json.get( + "entity_tags", + [["" for _ in entity_substr_list] for entity_substr_list in entity_substr_batch], + ) + context_batch = request.json.get("context", [[""]]) + prex_info_batch = request.json.get("property_extraction", [{} for _ in entity_substr_batch]) + + # Preprocess the conversation context + optimized_context_batch = preprocess_context(context_batch) + + entity_info_batch = [] + try: + ( + entity_substr_batch, + entity_ids_batch, + conf_batch, + entity_id_tags_batch, + ) = el(user_ids, entity_substr_batch, entity_tags_batch) + + # Process entity information + entity_info_batch = process_entity_info( + entity_substr_batch, + entity_ids_batch, + conf_batch, + entity_id_tags_batch, + prex_info_batch, + optimized_context_batch, + ) + + except Exception as e: + sentry_sdk.capture_exception(e) + logger.exception(e) + entity_info_batch = [[]] * len(entity_substr_batch) + + total_time = time.time() - st_time + logger.info(f"entity_info_batch: {entity_info_batch}") + logger.info(f"custom entity linking exec time = {total_time:.3f}s") + + # Return the processed entity information + return jsonify(entity_info_batch) + + +if __name__ == "__main__": + app.run(debug=False, host="0.0.0.0", port=3000) diff --git a/annotators/custom_entity_linking/service_configs/custom-entity-linking/environment.yml b/annotators/custom_entity_linking/service_configs/custom-entity-linking/environment.yml new file mode 100644 index 0000000000..e4ec5abba3 --- /dev/null +++ b/annotators/custom_entity_linking/service_configs/custom-entity-linking/environment.yml @@ -0,0 +1,5 @@ +CONFIG: custom_entity_linking.json +SERVICE_PORT: 8153 +SRC_DIR: annotators/custom_entity_linking/ +SERVICE_NAME: custom_entity_linking +FLASK_APP: server diff --git a/annotators/custom_entity_linking/service_configs/custom-entity-linking/service.yml b/annotators/custom_entity_linking/service_configs/custom-entity-linking/service.yml new file mode 100644 index 0000000000..a0f1fe51dd --- /dev/null +++ b/annotators/custom_entity_linking/service_configs/custom-entity-linking/service.yml @@ -0,0 +1,26 @@ +name: custom-entity-linking +endpoints: +- model +- add_entities +compose: + env_file: + - .env + build: + args: + CONFIG: custom_entity_linking.json + PORT: 8153 + SRC_DIR: annotators/custom_entity_linking + context: ./ + dockerfile: annotators/custom_entity_linking/Dockerfile + deploy: + resources: + limits: + memory: 128M + reservations: + memory: 128M + volumes: + - "./annotators/custom_entity_linking:/src" + - "~/.deeppavlov:/root/.deeppavlov" + ports: + - 8153:8153 +proxy: null diff --git a/annotators/custom_entity_linking/src/entity_linking.py b/annotators/custom_entity_linking/src/entity_linking.py new file mode 100644 index 0000000000..44187b2210 --- /dev/null +++ b/annotators/custom_entity_linking/src/entity_linking.py @@ -0,0 +1,263 @@ +# Copyright 2017 Neural Networks and Deep Learning lab, MIPT +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re +import sqlite3 +from logging import getLogger +from typing import List +from collections import defaultdict + +import nltk +from nltk.corpus import stopwords +from rapidfuzz import fuzz + +from deeppavlov.core.common.registry import register +from deeppavlov.core.models.component import Component +from deeppavlov.core.models.serializable import Serializable + +log = getLogger(__name__) + + +@register("entity_linker") +class EntityLinker(Component, Serializable): + """ + Class for linking of entity substrings in the document to entities in Wikidata + """ + + def __init__( + self, + load_path: str, + num_entities_to_return: int = 10, + lang: str = "en", + **kwargs, + ) -> None: + """ + Args: + load_path: path to folder with inverted index files + entity_ranker: component deeppavlov.models.kbqa.rel_ranking_bert + num_entities_for_bert_ranking: number of candidate entities for BERT ranking using description and context + ngram_range: char ngrams range for TfidfVectorizer + num_entities_to_return: number of candidate entities for the substring which are returned + lang: russian or english + use_description: whether to perform entity ranking by context and description + **kwargs: + """ + super().__init__(save_path=None, load_path=load_path) + self.num_entities_to_return = num_entities_to_return + self.lang = f"@{lang}" + if self.lang == "@en": + self.stopwords = set(stopwords.words("english")) + elif self.lang == "@ru": + self.stopwords = set(stopwords.words("russian")) + self.re_tokenizer = re.compile(r"[\w']+|[^\w ]") + self.not_found_str = "not in wiki" + self.stemmer = nltk.PorterStemmer() + self.load() + + def load(self) -> None: + if not os.path.exists(self.load_path): + os.makedirs(self.load_path) + self.conn = sqlite3.connect(str(self.load_path / "custom_database.db"), check_same_thread=False) + self.cur = self.conn.cursor() + self.cur.execute( + "CREATE VIRTUAL TABLE IF NOT EXISTS inverted_index USING fts5(title, entity_id, num_rels " + "UNINDEXED, tag, user_id, tokenize = 'porter ascii');" + ) + + def save(self) -> None: + pass + + def add_custom_entities(self, user_id, entity_substr_list, entity_ids_list, tags_list): + if self.conn is None: + if not os.path.exists(self.load_path): + os.makedirs(self.load_path) + self.conn = sqlite3.connect(str(self.load_path / "custom_database.db"), check_same_thread=False) + self.cur = self.conn.cursor() + self.cur.execute( + "CREATE VIRTUAL TABLE IF NOT EXISTS inverted_index USING fts5(title, entity_id, num_rels " + "UNINDEXED, tag, user_id, tokenize = 'porter ascii');" + ) + + for entity_substr, entity_id, tag in zip(entity_substr_list, entity_ids_list, tags_list): + entity_id = entity_id.replace("/", "slash").replace("-", "hyphen") + query_str = f"title:{entity_substr} AND tag:{tag} AND user_id:{user_id}" + + query = "SELECT * FROM inverted_index WHERE inverted_index MATCH ?;" + res = self.cur.execute(query, (query_str,)).fetchall() + if res and res[0][3] == "name" and res[0][1] == entity_id and tag == "name": + query = "DELETE FROM inverted_index WHERE entity_id=? AND tag=? AND user_id=?;" + self.cur.execute(query, (entity_id, tag, user_id)) + self.cur.execute( + "INSERT INTO inverted_index " "VALUES (?, ?, ?, ?, ?);", + (entity_substr.lower(), entity_id, 1, tag, user_id), + ) + self.conn.commit() + elif not res: + self.cur.execute( + "INSERT INTO inverted_index " "VALUES (?, ?, ?, ?, ?);", + (entity_substr.lower(), entity_id, 1, tag, user_id), + ) + self.conn.commit() + + def __call__( + self, + user_ids: List[str], + entity_substr_batch: List[List[str]], + entity_tags_batch: List[List[str]] = None, + ): + entity_ids_batch, entity_conf_batch, entity_id_tags_batch = [], [], [] + for user_id, entity_substr_list, entity_tags_list in zip(user_ids, entity_substr_batch, entity_tags_batch): + entity_ids_list, entity_conf_list, entity_id_tags_list = self.link_entities( + user_id, + entity_substr_list, + entity_tags_list, + ) + log.info(f"user_id: {user_id} entity_ids_list: {entity_ids_list} entity_conf_list: {entity_conf_list}") + + entity_ids_batch.append(entity_ids_list[: self.num_entities_to_return]) + entity_conf_batch.append(entity_conf_list[: self.num_entities_to_return]) + entity_id_tags_batch.append(entity_id_tags_list[: self.num_entities_to_return]) + return entity_ids_batch, entity_conf_batch, entity_id_tags_batch + + def link_entities( + self, + user_id: str, + entity_substr_list: List[str], + entity_tags_list: List[str], + ) -> List[List[str]]: + log.info(f"entity_substr_list {entity_substr_list} entity_tags_list {entity_tags_list} ") + entity_ids_list, conf_list, entity_id_tags_list = [], [], [] + if entity_substr_list: + for entity_substr, tags in zip(entity_substr_list, entity_tags_list): + for symb_old, symb_new in [ + ("'", "''"), + ("-", " "), + ("@", ""), + (".", ""), + (" ", " "), + ]: + entity_substr = entity_substr.replace(symb_old, symb_new) + cand_ent_init = defaultdict(set) + if len(entity_substr) > 1: + for start in ["a ", "the ", "my ", "his ", "her "]: + if entity_substr.startswith(start): + entity_substr = entity_substr[len(start) :] + cand_ent_init = self.find_exact_match(user_id, entity_substr, tags) + entity_substr_split = [ + word for word in entity_substr.split(" ") if word not in self.stopwords and len(word) > 0 + ] + if len(entity_substr_split) == 1 and self.stemmer.stem(entity_substr) != entity_substr: + entity_substr_stemmed = self.stemmer.stem(entity_substr) + stem_cand_ent_init = self.find_exact_match(user_id, entity_substr_stemmed, tags) + cand_ent_init = {**cand_ent_init, **stem_cand_ent_init} + if not cand_ent_init and len(entity_substr_split) > 1: + cand_ent_init = self.find_fuzzy_match(user_id, entity_substr_split, tags) + if not cand_ent_init: + cand_ent_init = self.find_exact_match(user_id, entity_substr) + if not cand_ent_init: + cand_ent_init = self.find_fuzzy_match(user_id, entity_substr_split) + + cand_ent_scores = [] + for entity in cand_ent_init: + entities_scores = list(cand_ent_init[entity]) + entities_scores = sorted(entities_scores, key=lambda x: (x[0], x[2], x[1]), reverse=True) + cand_ent_scores.append(([entity] + list(entities_scores[0]))) + + cand_ent_scores = sorted(cand_ent_scores, key=lambda x: (x[1], x[3], x[2]), reverse=True) + entity_ids = [elem[0] for elem in cand_ent_scores] + confs = [elem[1:4] for elem in cand_ent_scores] + entity_id_tags = [elem[4] for elem in cand_ent_scores] + entity_ids = [entity_id.replace("slash", "/").replace("hyphen", "-") for entity_id in entity_ids] + entity_ids_list.append(entity_ids) + conf_list.append(confs) + entity_id_tags_list.append(entity_id_tags) + + return entity_ids_list, conf_list, entity_id_tags_list + + def process_cand_ent(self, cand_ent_init, entities_and_ids, entity_substr_split, tags): + if tags: + for ( + entity_title, + entity_id, + entity_rels, + f_tag, + user_id, + ) in entities_and_ids: + for tag, tag_conf in tags: + if tag == f_tag: + substr_score = self.calc_substr_score(entity_title, entity_substr_split) + cand_ent_init[entity_id].add((substr_score, entity_rels, tag_conf, f_tag)) + else: + for ( + entity_title, + entity_id, + entity_rels, + f_tag, + user_id, + ) in entities_and_ids: + substr_score = self.calc_substr_score(entity_title, entity_substr_split) + cand_ent_init[entity_id].add((substr_score, entity_rels, 1.0, f_tag)) + return cand_ent_init + + def find_exact_match(self, user_id, entity_substr, tags=None): + entity_substr = entity_substr.lower() + entity_substr_split = entity_substr.split() + cand_ent_init = defaultdict(set) + + query_str = f"title:{entity_substr} AND user_id:{user_id}" + query = "SELECT * FROM inverted_index WHERE inverted_index MATCH ?;" + res = self.cur.execute(query, (query_str,)) + entities_and_ids = res.fetchall() + + if entities_and_ids: + cand_ent_init = self.process_cand_ent(cand_ent_init, entities_and_ids, entity_substr_split, tags) + return cand_ent_init + + def find_fuzzy_match(self, user_id, entity_substr_split, tags=None): + entity_substr_split = [word.lower() for word in entity_substr_split] + cand_ent_init = defaultdict(set) + for word in entity_substr_split: + query_str = f"title:{word} AND user_id:{user_id}" + query = "SELECT * FROM inverted_index WHERE inverted_index MATCH ?;" + res = self.cur.execute(query, (query_str,)) + part_entities_and_ids = res.fetchall() + cand_ent_init = self.process_cand_ent(cand_ent_init, part_entities_and_ids, entity_substr_split, tags) + return cand_ent_init + + def calc_substr_score(self, entity_title, entity_substr_split): + label_tokens = entity_title.split() + cnt = 0.0 + for ent_tok in entity_substr_split: + found = False + for label_tok in label_tokens: + if label_tok == ent_tok: + found = True + break + if found: + cnt += 1.0 + else: + for label_tok in label_tokens: + if label_tok[:2] == ent_tok[:2]: + fuzz_score = fuzz.ratio(label_tok, ent_tok) + if fuzz_score >= 80.0 and not found: + cnt += fuzz_score * 0.01 + break + substr_score = round(cnt / max(len(label_tokens), len(entity_substr_split)), 3) + if len(label_tokens) == 2 and len(entity_substr_split) == 1: + if entity_substr_split[0] == label_tokens[1]: + substr_score = 0.5 + elif entity_substr_split[0] == label_tokens[0]: + substr_score = 0.3 + return substr_score diff --git a/annotators/custom_entity_linking/test.sh b/annotators/custom_entity_linking/test.sh new file mode 100755 index 0000000000..ff0c94675f --- /dev/null +++ b/annotators/custom_entity_linking/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python test_el.py diff --git a/annotators/custom_entity_linking/test_el.py b/annotators/custom_entity_linking/test_el.py new file mode 100644 index 0000000000..8976a4433e --- /dev/null +++ b/annotators/custom_entity_linking/test_el.py @@ -0,0 +1,48 @@ +import requests + +use_context = True + + +def main(): + url = "http://0.0.0.0:8153" + inserted_data = { + "user_id": "1234", + "entity_info": { + "entity_substr": ["forrest gump"], + "entity_ids": ["film/123"], + "tags": ["film"], + }, + } + requests.post(f"{url}/add_entities", json=inserted_data) + + request_data = [ + { + "user_id": ["1234"], + "entity_substr": [["forrest gump"]], + "entity_tags": [[[("film", 1.0)]]], + "context": [["who directed forrest gump?"]], + } + ] + gold_results = [["film/123"]] + + count = 0 + for data, gold_result in zip(request_data, gold_results): + result = requests.post(f"{url}/model", json=data).json() + print(result) + + entity_ids = [] + for entity_info_list in result: + for entity_info in entity_info_list: + entity_ids = entity_info.get("entity_ids") + + if entity_ids == gold_result: + count += 1 + else: + print(f"Got {result}, but expected: {gold_result}") + + assert count == len(request_data) + print("Success") + + +if __name__ == "__main__": + main() diff --git a/assistant_dists/dream_kg/cpu.yml b/assistant_dists/dream_kg/cpu.yml new file mode 100644 index 0000000000..d8cdbd39df --- /dev/null +++ b/assistant_dists/dream_kg/cpu.yml @@ -0,0 +1,26 @@ +version: '3.7' +services: + ner: + environment: + DEVICE: cpu + CUDA_VISIBLE_DEVICES: "" + spacy-nounphrases: + environment: + CUDA_VISIBLE_DEVICES: "" + combined-classification: + environment: + CUDA_VISIBLE_DEVICES: "" + entity-detection: + environment: + CUDA_VISIBLE_DEVICES: "" + convers-evaluator-annotator: + environment: + DEVICE: cpu + CUDA_VISIBLE_DEVICES: "" + custom-entity-linking: + environment: + CUDA_VISIBLE_DEVICES: "" + transformers-lm-gptjt: + environment: + DEVICE: cpu + CUDA_VISIBLE_DEVICES: "" diff --git a/assistant_dists/dream_kg/db_conf.json b/assistant_dists/dream_kg/db_conf.json new file mode 100644 index 0000000000..a9ba6813f5 --- /dev/null +++ b/assistant_dists/dream_kg/db_conf.json @@ -0,0 +1,6 @@ +{ + "host": "DB_HOST", + "port": "DB_PORT", + "name": "DB_NAME", + "env": true +} \ No newline at end of file diff --git a/assistant_dists/dream_kg/dev.yml b/assistant_dists/dream_kg/dev.yml new file mode 100644 index 0000000000..f1b28504ae --- /dev/null +++ b/assistant_dists/dream_kg/dev.yml @@ -0,0 +1,107 @@ +# С такими volumes удобно дебажить, не нужно пересобирать контейнер каждый раз при изменении кода +services: + agent: + volumes: + - ".:/dp-agent" + ports: + - 4242:4242 + convers-evaluator-annotator: + volumes: + - "./annotators/ConversationEvaluator:/src" + - "~/.deeppavlov:/root/.deeppavlov" + ports: + - 8004:8004 + spacy-nounphrases: + volumes: + - "./annotators/spacy_nounphrases:/src" + - "./common:/src/common" + ports: + - 8006:8006 + sentseg: + volumes: + - "./annotators/SentSeg:/src" + ports: + - 8011:8011 + convers-evaluation-selector: + volumes: + - "./response_selectors/convers_evaluation_based_selector:/src" + - "./common:/src/common" + ports: + - 8009:8009 + badlisted-words: + volumes: + - "./annotators/BadlistedWordsDetector:/src" + - "./common:/src/common" + ports: + - 8018:8018 + ner: + volumes: + - './annotators/NER_deeppavlov:/src' + - "~/.deeppavlov:/root/.deeppavlov" + ports: + - 8021:8021 + entity-linking: + volumes: + - "./annotators/entity_linking:/src" + - "~/.deeppavlov:/root/.deeppavlov" + - "~/.deeppavlov/cache:/root/.cache" + ports: + - 8075:8075 + mongo: + ports: + - 27017:27017 + # # you can use persistent local volume if you need + # volumes: + # - ./venv/data/db_data:/root/data/db + spelling-preprocessing: + volumes: + - "./annotators/spelling_preprocessing:/src" + ports: + - 8074:8074 + combined-classification: + volumes: + - "./common:/src/common" + - "./annotators/combined_classification:/src" + - "~/.deeppavlov:/root/.deeppavlov" + - "~/.deeppavlov/cache:/root/.cache" + ports: + - 8087:8087 + entity-detection: + volumes: + - "./annotators/entity_detection:/src" + - "~/.deeppavlov:/root/.deeppavlov" + - "~/.deeppavlov/cache:/root/.cache" + ports: + - 8103:8103 + property-extraction: + volumes: + - "./annotators/property_extraction:/src" + - "~/.deeppavlov:/root/.deeppavlov" + ports: + - 8136:8136 + custom-entity-linking: + volumes: + - "./annotators/custom_entity_linking:/src" + - "~/.deeppavlov:/root/.deeppavlov" + ports: + - 8153:8153 + transformers-lm-gptjt: + volumes: + - "./services/transformers_lm:/src" + - "./common:/src/common" + - "~/.deeppavlov/cache:/root/.cache" + ports: + - 8161:8161 + dff-dream-persona-gpt-jt-prompted-skill: + volumes: + - "./skills/dff_template_prompted_skill:/src" + - "./common:/src/common" + ports: + - 8134:8134 + dff-template-skill: + volumes: + - "./skills/dff_template_skill:/src" + - "./common:/src/common" + ports: + - 8120:8120 +version: "3.7" diff --git a/assistant_dists/dream_kg/docker-compose.override.yml b/assistant_dists/dream_kg/docker-compose.override.yml new file mode 100644 index 0000000000..1d8364432e --- /dev/null +++ b/assistant_dists/dream_kg/docker-compose.override.yml @@ -0,0 +1,314 @@ +services: + agent: + command: sh -c 'bin/wait && python -m deeppavlov_agent.run agent.pipeline_config=assistant_dists/dream_kg/pipeline_conf.json' + environment: + WAIT_HOSTS: "convers-evaluator-annotator:8004, + spacy-nounphrases:8006, sentseg:8011, convers-evaluation-selector:8009, + badlisted-words:8018, ner:8021, spelling-preprocessing:8074, entity-linking:8075, + combined-classification:8087, entity-detection:8103, + property-extraction:8136, custom-entity-linking:8153, + transformers-lm-gptjt:8161, dff-dream-persona-gpt-jt-prompted-skill:8134, + dff-template-skill:8120" + WAIT_HOSTS_TIMEOUT: ${WAIT_TIMEOUT:-480} + HIGH_PRIORITY_INTENTS: 1 + RESTRICTION_FOR_SENSITIVE_CASE: 1 + ALWAYS_TURN_ON_ALL_SKILLS: 0 + LANGUAGE: EN + + convers-evaluator-annotator: + env_file: [ .env ] + build: + args: + CONFIG: conveval.json + SERVICE_PORT: 8004 + DATA_URL: https://files.deeppavlov.ai/alexaprize_data/cobot_conveval2.tar.gz + context: . + dockerfile: ./annotators/ConversationEvaluator/Dockerfile + environment: + - CUDA_VISIBLE_DEVICES=0 + deploy: + mode: replicated + replicas: 1 + resources: + limits: + memory: 2G + reservations: + memory: 2G + + spacy-nounphrases: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8006 + SERVICE_NAME: spacy_nounphrases + context: . + dockerfile: ./annotators/spacy_nounphrases/Dockerfile + command: flask run -h 0.0.0.0 -p 8006 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 256M + reservations: + memory: 256M + + sentseg: + env_file: [ .env ] + build: + context: ./annotators/SentSeg/ + command: flask run -h 0.0.0.0 -p 8011 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 1.5G + reservations: + memory: 1.5G + + convers-evaluation-selector: + env_file: [ .env ] + build: + args: + TAG_BASED_SELECTION: 1 + CALL_BY_NAME_PROBABILITY: 0.5 + PROMPT_PROBA: 0.1 + ACKNOWLEDGEMENT_PROBA: 0.3 + PRIORITIZE_WITH_REQUIRED_ACT: 0 + PRIORITIZE_NO_DIALOG_BREAKDOWN: 0 + PRIORITIZE_WITH_SAME_TOPIC_ENTITY: 0 + IGNORE_DISLIKED_SKILLS: 0 + GREETING_FIRST: 1 + RESTRICTION_FOR_SENSITIVE_CASE: 1 + PRIORITIZE_PROMTS_WHEN_NO_SCRIPTS: 1 + MAX_TURNS_WITHOUT_SCRIPTS: 7 + ADD_ACKNOWLEDGMENTS_IF_POSSIBLE: 1 + PRIORITIZE_SCRIPTED_SKILLS: 0 + CONFIDENCE_STRENGTH: 0.8 + CONV_EVAL_STRENGTH: 0.4 + PRIORITIZE_HUMAN_INITIATIVE: 1 + QUESTION_TO_QUESTION_DOWNSCORE_COEF: 0.8 + LANGUAGE: EN + context: . + dockerfile: ./response_selectors/convers_evaluation_based_selector/Dockerfile + command: flask run -h 0.0.0.0 -p 8009 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 100M + reservations: + memory: 100M + + badlisted-words: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8018 + SERVICE_NAME: badlisted_words + context: annotators/BadlistedWordsDetector/ + command: flask run -h 0.0.0.0 -p 8018 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 256M + reservations: + memory: 256M + + ner: + env_file: [ .env ] + build: + args: + CONFIG: ner_case_agnostic_multilingual_bert_base_extended.json + SERVICE_PORT: 8021 + SRC_DIR: annotators/NER_deeppavlov + COMMIT: f5117cd9ad1e64f6c2d970ecaa42fc09ccb23144 + context: ./ + dockerfile: annotators/NER_deeppavlov/Dockerfile + command: flask run -h 0.0.0.0 -p 8021 + environment: + - FLASK_APP=server + - CUDA_VISIBLE_DEVICES=0 + tty: true + deploy: + resources: + limits: + memory: 2G + reservations: + memory: 2G + + entity-linking: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8075 + SERVICE_NAME: entity_linking + CONFIG: entity_linking_eng.json + SRC_DIR: annotators/entity_linking + context: ./ + dockerfile: annotators/entity_linking/Dockerfile + environment: + - CUDA_VISIBLE_DEVICES=0 + deploy: + resources: + limits: + memory: 2.5G + reservations: + memory: 2.5G + + spelling-preprocessing: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8074 + SERVICE_NAME: spelling_preprocessing + context: ./annotators/spelling_preprocessing/ + command: flask run -h 0.0.0.0 -p 8074 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 100M + reservations: + memory: 100M + + combined-classification: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8087 + SERVICE_NAME: combined_classification + CONFIG: combined_classifier.json + context: . + dockerfile: ./annotators/combined_classification/Dockerfile + environment: + - CUDA_VISIBLE_DEVICES=0 + deploy: + resources: + limits: + memory: 2G + reservations: + memory: 2G + + entity-detection: + env_file: [ .env ] + build: + args: + SERVICE_NAME: entity_detection + SEQ_TAG_CONFIG: wikipedia_entity_detection_distilbert.json + CONFIG: entity_detection_eng.json + LOWERCASE: 1 + SERVICE_PORT: 8103 + SRC_DIR: annotators/entity_detection/ + FINEGRAINED: 0 + context: ./ + dockerfile: annotators/entity_detection/Dockerfile + command: flask run -h 0.0.0.0 -p 8103 + environment: + - FLASK_APP=server + - CUDA_VISIBLE_DEVICES=0 + deploy: + resources: + limits: + memory: 2.5G + reservations: + memory: 2.5G + + property-extraction: + env_file: [.env] + build: + args: + CONFIG: t5_generative_ie_lite_infer.json + SERVICE_PORT: 8136 + SRC_DIR: annotators/property_extraction/ + SERVICE_NAME: property_extraction + context: ./ + dockerfile: annotators/property_extraction/Dockerfile + command: flask run -h 0.0.0.0 -p 8136 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 7G + reservations: + memory: 7G + + custom-entity-linking: + env_file: [.env] + build: + args: + CONFIG: custom_entity_linking.json + PORT: 8153 + SRC_DIR: annotators/custom_entity_linking + context: ./ + dockerfile: annotators/custom_entity_linking/Dockerfile + deploy: + resources: + limits: + memory: 128M + reservations: + memory: 128M + + transformers-lm-gptjt: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8161 + SERVICE_NAME: transformers_lm_gptjt + PRETRAINED_MODEL_NAME_OR_PATH: togethercomputer/GPT-JT-6B-v1 + HALF_PRECISION: 0 + context: . + dockerfile: ./services/transformers_lm/Dockerfile + command: flask run -h 0.0.0.0 -p 8161 + environment: + - CUDA_VISIBLE_DEVICES=0 + - FLASK_APP=server + deploy: + resources: + limits: + memory: 50G + reservations: + memory: 50G + + dff-dream-persona-gpt-jt-prompted-skill: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8134 + SERVICE_NAME: dff_dream_persona_prompted_skill + PROMPT_FILE: common/prompts/dream_persona.json + GENERATIVE_SERVICE_URL: http://transformers-lm-gptjt:8161/respond + GENERATIVE_SERVICE_CONFIG: default_generative_config.json + GENERATIVE_TIMEOUT: 120 + N_UTTERANCES_CONTEXT: 7 + context: . + dockerfile: ./skills/dff_template_prompted_skill/Dockerfile + deploy: + resources: + limits: + memory: 128M + reservations: + memory: 128M + + dff-template-skill: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8120 + SERVICE_NAME: dff_template_skill + context: . + dockerfile: ./skills/dff_template_skill/Dockerfile + command: gunicorn --workers=1 server:app -b 0.0.0.0:8120 --reload + deploy: + resources: + limits: + memory: 128M + reservations: + memory: 128M +version: '3.7' diff --git a/assistant_dists/dream_kg/gpu1.yml b/assistant_dists/dream_kg/gpu1.yml new file mode 100644 index 0000000000..d51c3ce3ef --- /dev/null +++ b/assistant_dists/dream_kg/gpu1.yml @@ -0,0 +1,58 @@ +services: + agent: + restart: unless-stopped + volumes: + - "/cephfs/home/ignatov/artifacts:/output" + - ".:/dp-agent" + ports: + - ${AGENT_PORT}:4242 + combined-classification: + restart: unless-stopped + environment: + - CUDA_VISIBLE_DEVICES=7 + mongo: + restart: unless-stopped + command: mongod + image: mongo:4.0.0 + # # you can use persistent local volume if you need + # volumes: + # - ./venv/data/db_data:/root/data/db + sentseg: + restart: unless-stopped + convers-evaluation-selector: + restart: unless-stopped + badlisted-words: + restart: unless-stopped + ner: + restart: unless-stopped + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + environment: + - CUDA_VISIBLE_DEVICES=7 + entity-linking: + restart: unless-stopped + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + spelling-preprocessing: + restart: unless-stopped + entity-detection: + restart: unless-stopped + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + environment: + - CUDA_VISIBLE_DEVICES=9 + convers-evaluator-annotator: + restart: unless-stopped + environment: + - CUDA_VISIBLE_DEVICES=6 + transformers-lm-gptjt: + restart: unless-stopped + environment: + - CUDA_VISIBLE_DEVICES=0 + dff-template-skill: + restart: unless-stopped + property-extraction: + restart: unless-stopped + volumes: + - "~/.deeppavlov:/root/.deeppavlov" +version: '3.7' diff --git a/assistant_dists/dream_kg/pipeline_conf.json b/assistant_dists/dream_kg/pipeline_conf.json new file mode 100644 index 0000000000..567a10c29a --- /dev/null +++ b/assistant_dists/dream_kg/pipeline_conf.json @@ -0,0 +1,536 @@ +{ + "connectors": { + "sentseg": { + "protocol": "http", + "timeout": 1.5, + "url": "http://sentseg:8011/sentseg" + }, + "ner": { + "protocol": "http", + "timeout": 1.5, + "url": "http://ner:8021/ner" + } + }, + "services": { + "last_chance_service": { + "connector": { + "protocol": "python", + "class_name": "PredefinedTextConnector", + "response_text": "Sorry, something went wrong inside. Please tell me, what did you say.", + "annotations": { + "sentseg": { + "punct_sent": "Sorry, something went wrong inside. Please tell me, what did you say.", + "segments": [ + "Sorry, something went wrong inside.", + "Please tell me, what did you say." + ] + }, + "ner": [ + [] + ] + } + }, + "state_manager_method": "add_bot_utterance_last_chance", + "tags": [ + "last_chance" + ], + "is_enabled": true, + "source": { + "component": "components/sbDcAqiNqxFz.yml", + "service": "services/agent_services/service_configs/dream" + } + }, + "timeout_service": { + "connector": { + "protocol": "python", + "class_name": "PredefinedTextConnector", + "response_text": "Sorry, I need to think more on that. Let's talk about something else.", + "annotations": { + "sentseg": { + "punct_sent": "Sorry, I need to think more on that. Let's talk about something else.", + "segments": [ + "Sorry, I need to think more on that.", + "Let's talk about something else." + ] + }, + "ner": [ + [] + ] + } + }, + "state_manager_method": "add_bot_utterance_last_chance", + "tags": [ + "timeout" + ], + "is_enabled": true, + "source": { + "component": "components/rFC0YJOoDFvS.yml", + "service": "services/agent_services/service_configs/dream" + } + }, + "annotators": { + "spelling_preprocessing": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://spelling-preprocessing:8074/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:last_utt_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [], + "state_manager_method": "add_annotation_and_reset_human_attributes_for_first_turn", + "is_enabled": true, + "source": { + "component": "components/pGxj32ic41pvquRXUdqc7A.yml", + "service": "annotators/spelling_preprocessing/service_configs/spelling-preprocessing" + } + }, + "sentseg": { + "connector": { + "protocol": "http", + "timeout": 1.5, + "url": "http://sentseg:8011/sentseg" + }, + "dialog_formatter": "state_formatters.dp_formatters:preproc_last_human_utt_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.spelling_preprocessing" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/gM4fEjvVqLlSRRRkQfds2g.yml", + "service": "annotators/SentSeg/service_configs/sentseg" + } + }, + "spacy_nounphrases": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://spacy-nounphrases:8006/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:preproc_last_human_utt_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.spelling_preprocessing" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/dswI5sRZbFPmgcNQKH5iPg.yml", + "service": "annotators/spacy_nounphrases/service_configs/spacy-nounphrases" + } + }, + "badlisted_words": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://badlisted-words:8018/badlisted_words" + }, + "dialog_formatter": "state_formatters.dp_formatters:preproc_last_human_utt_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.spelling_preprocessing" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/gySZ51dyqYi9TOFr6zY5kA.yml", + "service": "annotators/BadlistedWordsDetector/service_configs/badlisted-words" + } + }, + "ner": { + "connector": { + "protocol": "http", + "timeout": 1.5, + "url": "http://ner:8021/ner" + }, + "dialog_formatter": "state_formatters.dp_formatters:ner_formatter_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.spelling_preprocessing", + "annotators.sentseg" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/3RDNPBdybjBlSQZqcc7nGQ.yml", + "service": "annotators/NER_deeppavlov/service_configs/ner" + } + }, + "entity_detection": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://entity-detection:8103/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:entity_detection_formatter_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.spelling_preprocessing", + "annotators.sentseg" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/05PqJXVd7gV7DqslN5z3A.yml", + "service": "annotators/entity_detection/service_configs/entity-detection" + } + }, + "entity_linking": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://entity-linking:8075/model" + }, + "dialog_formatter": "state_formatters.dp_formatters:el_formatter_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.ner", + "annotators.entity_detection", + "annotators.spacy_nounphrases" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/M1sE6hOm20EGBWBdr0vIOw.yml", + "service": "annotators/entity_linking/service_configs/entity-linking" + } + }, + "property_extraction": { + "connector": { + "protocol": "http", + "url": "http://property-extraction:8136/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:property_extraction_formatter_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.ner", + "annotators.entity_linking" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/7v330b9odypuiugqeiuqij.yml", + "service": "annotators/property_extraction/service_configs/property-extraction" + } + }, + "custom_entity_linking": { + "connector": { + "protocol": "http", + "url": "http://custom-entity-linking:8153/model" + }, + "dialog_formatter": "state_formatters.dp_formatters:custom_el_formatter_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.ner", + "annotators.entity_detection", + "annotators.spacy_nounphrases", + "annotators.property_extraction" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/7v330b9odypuiugqeiuqij.yml", + "service": "annotators/custom_entity_linking/service_configs/custom-entity-linking" + } + }, + "combined_classification": { + "connector": { + "protocol": "http", + "timeout": 3.0, + "url": "http://combined-classification:8087/model" + }, + "dialog_formatter": "state_formatters.dp_formatters:preproc_last_human_utt_dialog_w_hist", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators.spelling_preprocessing" + ], + "state_manager_method": "add_annotation", + "is_enabled": true, + "source": { + "component": "components/PbLNvh4hrvs47rPaf2bfYQ.yml", + "service": "annotators/combined_classification/service_configs/combined-classification" + } + } + }, + "response_annotators": { + "sentseg": { + "connector": { + "protocol": "http", + "timeout": 1.5, + "url": "http://sentseg:8011/sentseg" + }, + "dialog_formatter": "state_formatters.dp_formatters:last_bot_utt_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "response_annotator_selectors" + ], + "state_manager_method": "add_annotation_prev_bot_utt", + "is_enabled": true, + "source": { + "component": "components/1Q9QXih1U2zhCpVm9zxdsA.yml", + "service": "annotators/SentSeg/service_configs/sentseg" + } + }, + "ner": { + "connector": { + "protocol": "http", + "timeout": 1.5, + "url": "http://ner:8021/ner" + }, + "dialog_formatter": "state_formatters.dp_formatters:ner_formatter_last_bot_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "response_annotator_selectors", + "response_annotators.sentseg" + ], + "state_manager_method": "add_annotation_prev_bot_utt", + "is_enabled": true, + "source": { + "component": "components/3RDNPBdybjBlSQZqcc7nGQ.yml", + "service": "annotators/NER_deeppavlov/service_configs/ner" + } + }, + "spacy_nounphrases": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://spacy-nounphrases:8006/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:last_bot_utt_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "skills" + ], + "state_manager_method": "add_annotation_prev_bot_utt", + "is_enabled": true, + "source": { + "component": "components/dswI5sRZbFPmgcNQKH5iPg.yml", + "service": "annotators/spacy_nounphrases/service_configs/spacy-nounphrases" + } + } + }, + "response_annotator_selectors": { + "connector": { + "protocol": "python", + "class_name": "skill_selectors.post_annotator_selector.connector:PostAnnotatorSelectorConnector", + "annotator_names": [ + "sentseg", + "ner", + "spacy_nounphrases" + ] + }, + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "tags": [ + "selector" + ], + "is_enabled": true, + "source": { + "component": "components/LXrJDIf43gwNmPMNXG5Eg.yml", + "service": "services/response_annotator_selectors/service_configs/agent" + } + }, + "candidate_annotators": { + "badlisted_words": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://badlisted-words:8018/badlisted_words_batch" + }, + "dialog_formatter": "state_formatters.dp_formatters:hypotheses_list", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "skills" + ], + "state_manager_method": "add_hypothesis_annotation_batch", + "is_enabled": true, + "source": { + "component": "components/gySZ51dyqYi9TOFr6zY5kA.yml", + "service": "annotators/BadlistedWordsDetector/service_configs/badlisted-words" + } + }, + "convers_evaluator_annotator": { + "connector": { + "protocol": "http", + "timeout": 2.0, + "url": "http://convers-evaluator-annotator:8004/batch_model" + }, + "dialog_formatter": "state_formatters.dp_formatters:convers_evaluator_annotator_formatter", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "skills" + ], + "state_manager_method": "add_hypothesis_annotation_batch", + "is_enabled": true, + "source": { + "component": "components/n1HuqlV7EoNrWXcv8WaIQ.yml", + "service": "annotators/ConversationEvaluator/service_configs/convers-evaluator-annotator" + } + }, + "spacy_nounphrases": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://spacy-nounphrases:8006/respond_batch" + }, + "dialog_formatter": "state_formatters.dp_formatters:hypotheses_list", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "skills" + ], + "state_manager_method": "add_hypothesis_annotation_batch", + "is_enabled": true, + "source": { + "component": "components/dswI5sRZbFPmgcNQKH5iPg.yml", + "service": "annotators/spacy_nounphrases/service_configs/spacy-nounphrases" + } + }, + "entity_detection": { + "connector": { + "protocol": "http", + "timeout": 2.0, + "url": "http://entity-detection:8103/respond_batch" + }, + "dialog_formatter": "state_formatters.dp_formatters:hypotheses_list", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "skills" + ], + "state_manager_method": "add_hypothesis_annotation_batch", + "is_enabled": true, + "source": { + "component": "components/05PqJXVd7gV7DqslN5z3A.yml", + "service": "annotators/entity_detection/service_configs/entity-detection" + } + }, + "combined_classification": { + "connector": { + "protocol": "http", + "timeout": 2.0, + "url": "http://combined-classification:8087/batch_model" + }, + "dialog_formatter": "state_formatters.dp_formatters:hypothesis_histories_list", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "skills" + ], + "state_manager_method": "add_hypothesis_annotation_batch", + "is_enabled": true, + "source": { + "component": "components/PbLNvh4hrvs47rPaf2bfYQ.yml", + "service": "annotators/combined_classification/service_configs/combined-classification" + } + } + }, + "skill_selectors": { + "rule_based_selector": { + "connector": { + "protocol": "python", + "class_name": "skill_selectors.rule_based_selector.connector:RuleBasedSkillSelectorConnector" + }, + "dialog_formatter": "state_formatters.dp_formatters:base_skill_selector_formatter_dialog", + "response_formatter": "state_formatters.dp_formatters:simple_formatter_service", + "previous_services": [ + "annotators" + ], + "tags": [ + "selector" + ], + "is_enabled": true, + "source": { + "component": "components/xSwFvtAUdvtQosvzpb7oMg.yml", + "service": "skill_selectors/rule_based_selector/service_configs/agent" + } + } + }, + "skills": { + "dff_dream_persona_prompted_skill": { + "connector": { + "protocol": "http", + "timeout": 120.0, + "url": "http://dff-dream-persona-gpt-j-prompted-skill:8134/respond" + }, + "dialog_formatter": { + "name": "state_formatters.dp_formatters:dff_prompted_skill_formatter", + "skill_name": "dff_dream_persona_prompted_skill" + }, + "response_formatter": "state_formatters.dp_formatters:skill_with_attributes_formatter_service", + "previous_services": [ + "skill_selectors" + ], + "state_manager_method": "add_hypothesis", + "is_enabled": true, + "source": { + "component": "components/6lLhP7zuoiI0bxJEDLWUg.yml", + "service": "skills/dff_template_prompted_skill/service_configs/dff-dream-persona-gpt-jt-prompted-skill" + } + }, + "dummy_skill": { + "connector": { + "protocol": "python", + "class_name": "skills.dummy_skill.connector:DummySkillConnector" + }, + "dialog_formatter": "state_formatters.dp_formatters:utt_sentrewrite_modified_last_dialog", + "response_formatter": "state_formatters.dp_formatters:skill_with_attributes_formatter_service", + "previous_services": [ + "skill_selectors" + ], + "state_manager_method": "add_hypothesis", + "is_enabled": true, + "source": { + "component": "components/uYkoK0vRp4bbIg9akI1yw.yml", + "service": "skills/dummy_skill/service_configs/agent" + } + }, + "dff_template_skill": { + "connector": { + "protocol": "http", + "timeout": 2.0, + "url": "http://dff-template-skill:8120/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:dff_template_skill_formatter", + "response_formatter": "state_formatters.dp_formatters:skill_with_attributes_formatter_service", + "previous_services": [ + "skill_selectors" + ], + "state_manager_method": "add_hypothesis", + "is_enabled": true, + "source": { + "component": "components/pC342KBWtheQNnXjIni6A.yml", + "service": "skills/dff_template_skill/service_configs/dff-template-skill" + } + } + }, + "response_selectors": { + "response_selector": { + "connector": { + "protocol": "http", + "timeout": 1.0, + "url": "http://convers-evaluation-selector:8009/respond" + }, + "dialog_formatter": "state_formatters.dp_formatters:full_history_dialog", + "response_formatter": "state_formatters.dp_formatters:base_response_selector_formatter_service", + "previous_services": [ + "candidate_annotators" + ], + "state_manager_method": "add_bot_utterance", + "is_enabled": true, + "source": { + "component": "components/ly2AVNtIcJpTWz1qJ1mvKQ.yml", + "service": "response_selectors/convers_evaluation_based_selector/service_configs/convers-evaluation-selector" + } + } + } + }, + "metadata": { + "display_name": "Dream KG", + "author": "DeepPavlov", + "description": "Distribution that utilizes Knowledge Graph", + "version": "0.1.0", + "date_created": "2022-12-12T12:12:00", + "ram_usage": "50 GB", + "gpu_usage": "50 GB", + "disk_usage": "50 GB" + } +} \ No newline at end of file diff --git a/assistant_dists/dream_kg/proxy.yml b/assistant_dists/dream_kg/proxy.yml new file mode 100644 index 0000000000..db1207358e --- /dev/null +++ b/assistant_dists/dream_kg/proxy.yml @@ -0,0 +1,118 @@ +services: + convers-evaluator-annotator: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8004 + - PORT=8004 + + sentseg: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8011 + - PORT=8011 + + badlisted-words: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8018 + - PORT=8018 + + ner: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8021 + - PORT=8021 + + spelling-preprocessing: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8074 + - PORT=8074 + + entity-linking: + command: [ "nginx", "-g", "daemon off;" ] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8075 + - PORT=8075 + + combined-classification: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8087 + - PORT=8087 + + entity-detection: + command: [ "nginx", "-g", "daemon off;" ] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8103 + - PORT=8103 + + spacy-nounphrases: + command: [ "nginx", "-g", "daemon off;" ] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8006 + - PORT=8006 + + convers-evaluation-selector: + command: [ "nginx", "-g", "daemon off;" ] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8009 + - PORT=8009 + + transformers-lm-gptjt: + command: ["nginx", "-g", "daemon off;"] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8161 + - PORT=8161 + + dff-template-skill: + command: [ "nginx", "-g", "daemon off;" ] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8120 + - PORT=8120 + + property-extraction: + command: [ "nginx", "-g", "daemon off;" ] + build: + context: dp/proxy/ + dockerfile: Dockerfile + environment: + - PROXY_PASS=proxy.deeppavlov.ai:8136 + - PORT=8136 +version: '3.7' diff --git a/assistant_dists/dream_kg/telegram.yml b/assistant_dists/dream_kg/telegram.yml new file mode 100644 index 0000000000..6bf8a53140 --- /dev/null +++ b/assistant_dists/dream_kg/telegram.yml @@ -0,0 +1,17 @@ +services: + agent-tg: + command: sh -c 'bin/wait && python -m deeppavlov_agent.run agent.channel=telegram agent.telegram_token=$TG_TOKEN agent.pipeline_config=assistant_dists/dream_kg/pipeline_conf.json agent.db_config=assistant_dists/dream_kg/db_conf.json' + env_file: [.env] + build: + context: ./ + dockerfile: dockerfile_agent + deploy: + resources: + limits: + memory: 4G + reservations: + memory: 2G + volumes: + - ".:/dp-agent" + +version: '3.7' diff --git a/assistant_dists/dream_kg/test.yml b/assistant_dists/dream_kg/test.yml new file mode 100644 index 0000000000..dc6251e97c --- /dev/null +++ b/assistant_dists/dream_kg/test.yml @@ -0,0 +1,46 @@ +services: + agent: + volumes: + - "/cephfs/home/ignatov/artifacts:/output" + ports: + - ${AGENT_PORT}:4242 + combined-classification: + environment: + - CUDA_VISIBLE_DEVICES=7 + mongo: + command: mongod + image: mongo:4.0.0 + # # you can use persistent local volume if you need + # volumes: + # - ./venv/data/db_data:/root/data/db + spacy-nounphrases: + sentseg: + convers-evaluation-selector: + badlisted-words: + ner: + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + environment: + - CUDA_VISIBLE_DEVICES=7 + entity-linking: + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + environment: + - CUDA_VISIBLE_DEVICES=9 + spelling-preprocessing: + entity-detection: + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + environment: + - CUDA_VISIBLE_DEVICES=8 + convers-evaluator-annotator: + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + environment: + - CUDA_VISIBLE_DEVICES=8 + dff-template-skill: + property-extraction: + volumes: + - "~/.deeppavlov:/root/.deeppavlov" + custom-entity-linking: +version: '3.7' diff --git a/components.tsv b/components.tsv index 90640c341d..789953d85f 100644 --- a/components.tsv +++ b/components.tsv @@ -154,7 +154,7 @@ 8150 combined-classification-lightweight 8151 dff-dream-persona-llama7bru-prompted-skill 8152 dff-deepy-prompted-skill -8153 +8153 custom-entity-linking 8154 dff-casual-email-prompted-skill 8155 dff-meeting-notes-prompted-skill 8156 dff-official-email-prompted-skill diff --git a/components/7v330b9odypuiugqeiuqij.yml b/components/7v330b9odypuiugqeiuqij.yml new file mode 100644 index 0000000000..f5a1b9f19f --- /dev/null +++ b/components/7v330b9odypuiugqeiuqij.yml @@ -0,0 +1,26 @@ +name: custom_entity_linking +display_name: Custom Entity Linking +component_type: null +model_type: null +is_customizable: false +author: publisher@deeppavlov.ai +description: finds entity ids in custom knowledge graph for the entities detected with Entity Detection +ram_usage: 128M +gpu_usage: null +group: annotators +connector: + protocol: http + url: http://custom-entity-linking:8153/model +dialog_formatter: state_formatters.dp_formatters:custom_el_formatter_dialog +response_formatter: state_formatters.dp_formatters:simple_formatter_service +previous_services: +- annotators.ner +- annotators.entity_detection +- annotators.spacy_nounphrases +- annotators.property_extraction +required_previous_services: null +state_manager_method: add_annotation +tags: null +endpoint: model +service: annotators/custom_entity_linking/service_configs/custom-entity-linking +date_created: '2023-06-02T13:45:33' diff --git a/state_formatters/dp_formatters.py b/state_formatters/dp_formatters.py index 0734ab3e49..21defbec9f 100755 --- a/state_formatters/dp_formatters.py +++ b/state_formatters/dp_formatters.py @@ -678,8 +678,7 @@ def wp_formatter_dialog(dialog: Dict): ] -def el_formatter_dialog(dialog: Dict): - # Used by: entity_linking annotator +def prepare_el_input(dialog: Dict): num_last_utterances = 2 entities_with_labels = get_entities(dialog["human_utterances"][-1], only_named=False, with_labels=True) entity_substr_list, entity_tags_list = [], [] @@ -693,21 +692,31 @@ def el_formatter_dialog(dialog: Dict): entity_tags_list.append([[entity["label"].lower(), 1.0]]) else: entity_tags_list.append([["misc", 1.0]]) - triplets = dialog["human_utterances"][-1]["annotations"].get("property_extraction", [{}]) - for triplet in triplets: - object_entity_substr = triplet.get("object", "") - if object_entity_substr and object_entity_substr not in entity_substr_list: - entity_substr_list.append(object_entity_substr) - entity_tags_list.append([["misc", 1.0]]) dialog = utils.get_last_n_turns(dialog, bot_last_turns=1) dialog = utils.replace_with_annotated_utterances(dialog, mode="punct_sent") - context = [[uttr["text"] for uttr in dialog["utterances"][-num_last_utterances:]]] + context = [uttr["text"] for uttr in dialog["utterances"][-num_last_utterances:]] + return entity_substr_list, entity_tags_list, context + + +def el_formatter_dialog(dialog: Dict): + # Used by: entity_linking annotator + entity_substr_list, entity_tags_list, context = prepare_el_input(dialog) + return [{"entity_substr": [entity_substr_list], "entity_tags": [entity_tags_list], "context": [context]}] + + +def custom_el_formatter_dialog(dialog: Dict): + # Used by: entity_linking annotator + entity_substr_list, entity_tags_list, context = prepare_el_input(dialog) + property_extraction = dialog["human_utterances"][-1]["annotations"].get("property_extraction", {}) + user_id = str(dialog["human_utterances"][-1].get("user", {}).get("id", "")) return [ { + "user_id": [user_id], "entity_substr": [entity_substr_list], "entity_tags": [entity_tags_list], - "context": context, + "context": [context], + "property_extraction": [property_extraction], } ] From cbe539cdf35edb18549ea11404fb950ed20e98af Mon Sep 17 00:00:00 2001 From: "Dilyara Zharikova (Baymurzina)" Date: Tue, 27 Jun 2023 11:03:01 +0300 Subject: [PATCH 06/11] Fix/config for transformers llm (#500) * fix: config for transformers llm * fix: requirements * fix: tests for transf * fix max_tokens; update default configs * style --------- Co-authored-by: Nika Smilga --- .../default_generative_config.json | 4 ++-- services/transformers_lm/requirements.txt | 2 +- services/transformers_lm/server.py | 15 ++------------- services/transformers_lm/test.py | 4 ++-- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/common/generative_configs/default_generative_config.json b/common/generative_configs/default_generative_config.json index cecd6ab7cb..96b804f7a7 100644 --- a/common/generative_configs/default_generative_config.json +++ b/common/generative_configs/default_generative_config.json @@ -1,6 +1,6 @@ { - "max_length": 120, - "min_length": 8, + "max_new_tokens": 120, + "min_new_tokens": 8, "top_p": 0.9, "temperature": 0.9, "do_sample": true, diff --git a/services/transformers_lm/requirements.txt b/services/transformers_lm/requirements.txt index 56a299fd1c..b1401de779 100644 --- a/services/transformers_lm/requirements.txt +++ b/services/transformers_lm/requirements.txt @@ -1,4 +1,4 @@ -transformers==4.25.1 +transformers==4.30.0 flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 diff --git a/services/transformers_lm/server.py b/services/transformers_lm/server.py index 7de34360d3..47197d9926 100644 --- a/services/transformers_lm/server.py +++ b/services/transformers_lm/server.py @@ -52,9 +52,6 @@ def generate_responses(context, model, tokenizer, prompt, generation_params, con else: dialog_context += "\n".join(context) + f"\n{NAMING[LANGUAGE][0]}:" - max_length = generation_params.get("max_length", 50) - generation_params.pop("max_length", None) - logger.info(f"context inside generate_responses seen as: {dialog_context}") bot_input_ids = tokenizer([dialog_context], return_tensors="pt").input_ids with torch.no_grad(): @@ -62,7 +59,6 @@ def generate_responses(context, model, tokenizer, prompt, generation_params, con bot_input_ids = bot_input_ids.to("cuda") chat_history_ids = model.generate( bot_input_ids, - max_length=len(tokenizer(dialog_context)["input_ids"]) + max_length, pad_token_id=tokenizer.eos_token_id, **generation_params, ) @@ -87,20 +83,13 @@ def generate_responses(context, model, tokenizer, prompt, generation_params, con if torch.cuda.is_available(): model.to("cuda") logger.info("transformers_lm is set to run on cuda") - default_config = { - "max_length": 60, - "min_length": 8, - "top_p": 0.9, - "temperature": 0.9, - "do_sample": True, - "num_return_sequences": 1, - } + config = DEFAULT_CONFIGS[PRETRAINED_MODEL_NAME_OR_PATH] example_response = generate_responses( ["What is the goal of SpaceX?"], model, tokenizer, "You are a SpaceX Assistant.", - default_config, + config, ) logger.info(f"example response: {example_response}") logger.info("transformers_lm is ready") diff --git a/services/transformers_lm/test.py b/services/transformers_lm/test.py index 43cf680fc8..f31cd86a8a 100644 --- a/services/transformers_lm/test.py +++ b/services/transformers_lm/test.py @@ -3,8 +3,8 @@ DEFAULT_CONFIG = { - "max_length": 60, - "min_length": 8, + "max_new_tokens": 60, + "min_new_tokens": 8, "top_p": 0.9, "temperature": 0.9, "do_sample": True, From 5ad76f51f0908bc3d27386f0296b1d0c7a761700 Mon Sep 17 00:00:00 2001 From: "Dilyara Zharikova (Baymurzina)" Date: Tue, 27 Jun 2023 14:53:28 +0300 Subject: [PATCH 07/11] Feat/anthropic to prod (#493) * feat: use new antropic models * feat: use new antropic models in universal * feat: empty config * feat: empty config --- MODELS.md | 24 ++++++----- .../universal_prompted_assistant/dev.yml | 12 ++++++ .../docker-compose.override.yml | 43 ++++++++++++++++--- .../empty_generative_config.json | 2 + components.tsv | 4 +- components/lkfjsdkur348rnjlkadfg12esdf.yml | 6 +-- components/njkhf834hfbg78sf.yml | 6 +-- services/anthropic_api_lm/server.py | 4 +- .../environment.yml | 4 ++ .../service.yml | 8 ++-- .../anthropic-api-claude-v1/environment.yml | 4 ++ .../service.yml | 8 ++-- .../anthropic-api-claudev1/environment.yml | 5 --- .../anthropic-api-claudev13/environment.yml | 5 --- .../scenario/response.py | 2 + 15 files changed, 91 insertions(+), 46 deletions(-) create mode 100644 common/generative_configs/empty_generative_config.json create mode 100644 services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/environment.yml rename services/anthropic_api_lm/service_configs/{anthropic-api-claudev13 => anthropic-api-claude-instant-v1}/service.yml (72%) create mode 100644 services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/environment.yml rename services/anthropic_api_lm/service_configs/{anthropic-api-claudev1 => anthropic-api-claude-v1}/service.yml (73%) delete mode 100644 services/anthropic_api_lm/service_configs/anthropic-api-claudev1/environment.yml delete mode 100644 services/anthropic_api_lm/service_configs/anthropic-api-claudev13/environment.yml diff --git a/MODELS.md b/MODELS.md index 4ed3243cb8..89e06290c4 100644 --- a/MODELS.md +++ b/MODELS.md @@ -2,14 +2,16 @@ Here you may find a list of models that currently available for use in Generative Assistants. -| model name | container name | model link | open-source? | size (billion parameters) | GPU usage | max tokens (prompt + response) | description | -|---------------------------|--------------------------|-------------------------------------------------------------------------|--------------------------|---------------------------|---------------------------|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| BLOOMZ 7B | transformers-lm-bloomz7b | [link](https://huggingface.co/bigscience/bloomz-7b1) | yes | 7.1B | 33GB | 2,048 tokens | An open-source multilingual instruction-based large language model (46 languages). NB: free of charge. This model is up and running on our servers and can be used for free. | -| GPT-J 6B | transformers-lm-gptj | [link](https://huggingface.co/EleutherAI/gpt-j-6b) | yes | 6B | 25GB | 2,048 tokens | An open-source English-only large language model which is NOT fine-tuned for instruction following and NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | -| GPT-3.5 | openai-api-davinci3 | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,097 tokens | A multulingual instruction-based large language model which is capable of code generation. Unlike ChatGPT, not optimised for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| ChatGPT | openai-api-chatgpt | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,096 tokens | Based on gpt-3.5-turbo -- the most capable of the entire GPT-3/GPT-3.5 models family. Optimized for chat. Able to understand and generate code. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| Open-Assistant Pythia 12B | transformers-lm-oasst12b | [link](https://huggingface.co/OpenAssistant/pythia-12b-sft-v8-7k-steps) | yes | 12B | 26GB (half-precision) | 5,120 tokens | An open-source English-only instruction-based large language model which is NOT good at answering math and coding questions. NB: free of charge. This model is up and running on our servers and can be used for free. | -| GPT-4 | openai-api-gpt4 | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 8,192 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. More capable than any GPT-3.5 model, able to do more complex tasks, and optimized for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| GPT-4 32K | openai-api-gpt4-32k | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 32,768 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. Same capabilities as the base gpt-4 mode but with 4x the context length. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| GPT-JT 6B | transformers-lm-gptjt | [link](https://huggingface.co/togethercomputer/GPT-JT-6B-v1) | yes | 6B | 26GB | 2,048 tokens | An open-source English-only large language model which was fine-tuned for instruction following but is NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | -| ChatGPT 16k | openai-api-chatgpt-16k | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 16,384 tokens | Same capabilities as the standard gpt-3.5-turbo model but with 4 times the context. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| model name | container name | model link | open-source? | size (billion parameters) | GPU usage | max tokens (prompt + response) | description | +|---------------------------|---------------------------------|-------------------------------------------------------------------------|--------------------------|---------------------------|---------------------------|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| BLOOMZ 7B | transformers-lm-bloomz7b | [link](https://huggingface.co/bigscience/bloomz-7b1) | yes | 7.1B | 33GB | 2,048 tokens | An open-source multilingual instruction-based large language model (46 languages). NB: free of charge. This model is up and running on our servers and can be used for free. | +| GPT-J 6B | transformers-lm-gptj | [link](https://huggingface.co/EleutherAI/gpt-j-6b) | yes | 6B | 25GB | 2,048 tokens | An open-source English-only large language model which is NOT fine-tuned for instruction following and NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | +| GPT-3.5 | openai-api-davinci3 | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,097 tokens | A multulingual instruction-based large language model which is capable of code generation. Unlike ChatGPT, not optimised for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| ChatGPT | openai-api-chatgpt | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,096 tokens | Based on gpt-3.5-turbo -- the most capable of the entire GPT-3/GPT-3.5 models family. Optimized for chat. Able to understand and generate code. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| Open-Assistant Pythia 12B | transformers-lm-oasst12b | [link](https://huggingface.co/OpenAssistant/pythia-12b-sft-v8-7k-steps) | yes | 12B | 26GB (half-precision) | 5,120 tokens | An open-source English-only instruction-based large language model which is NOT good at answering math and coding questions. NB: free of charge. This model is up and running on our servers and can be used for free. | +| GPT-4 | openai-api-gpt4 | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 8,192 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. More capable than any GPT-3.5 model, able to do more complex tasks, and optimized for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| GPT-4 32K | openai-api-gpt4-32k | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 32,768 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. Same capabilities as the base gpt-4 mode but with 4x the context length. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| GPT-JT 6B | transformers-lm-gptjt | [link](https://huggingface.co/togethercomputer/GPT-JT-6B-v1) | yes | 6B | 26GB | 2,048 tokens | An open-source English-only large language model which was fine-tuned for instruction following but is NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | +| ChatGPT 16k | openai-api-chatgpt-16k | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 16,384 tokens | Same capabilities as the standard gpt-3.5-turbo model but with 4 times the context. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| Anthropic Claude-v1 | anthropic-api-claude-v1 | [link](https://docs.anthropic.com/claude/reference/complete_post) | no (paid access via API) | | - (cannot be run locally) | 9,000 tokens | The largest model, ideal for a wide range of more complex tasks. NB: paid. You must provide your Anthropic API key to use the model. Your Anthropic API account will be charged according to your usage. | +| Anthropic Claude-v1 | anthropic-api-claude-instant-v1 | [link](https://docs.anthropic.com/claude/reference/complete_post) | no (paid access via API) | | - (cannot be run locally) | 9,000 tokens | A smaller model with far lower latency, sampling at roughly 40 words/sec! Its output quality is somewhat lower than the latest claude-1 model, particularly for complex tasks. However, it is much less expensive and blazing fast. NB: paid. You must provide your Anthropic API key to use the model. Your Anthropic API account will be charged according to your usage. | diff --git a/assistant_dists/universal_prompted_assistant/dev.yml b/assistant_dists/universal_prompted_assistant/dev.yml index 53bcb27708..8258507fd8 100644 --- a/assistant_dists/universal_prompted_assistant/dev.yml +++ b/assistant_dists/universal_prompted_assistant/dev.yml @@ -72,6 +72,18 @@ services: - "./common:/src/common" ports: - 8167:8167 + anthropic-api-claude-v1: + volumes: + - "./services/anthropic_api_lm:/src" + - "./common:/src/common" + ports: + - 8164:8164 + anthropic-api-claude-instant-v1: + volumes: + - "./services/anthropic_api_lm:/src" + - "./common:/src/common" + ports: + - 8163:8163 dff-universal-prompted-skill: volumes: - "./skills/dff_universal_prompted_skill:/src" diff --git a/assistant_dists/universal_prompted_assistant/docker-compose.override.yml b/assistant_dists/universal_prompted_assistant/docker-compose.override.yml index bf8e84b406..0c2fd45ad6 100644 --- a/assistant_dists/universal_prompted_assistant/docker-compose.override.yml +++ b/assistant_dists/universal_prompted_assistant/docker-compose.override.yml @@ -137,7 +137,6 @@ services: dockerfile: ./services/openai_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8145 environment: - - CUDA_VISIBLE_DEVICES=0 - FLASK_APP=server deploy: resources: @@ -157,7 +156,6 @@ services: dockerfile: ./services/openai_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8131 environment: - - CUDA_VISIBLE_DEVICES=0 - FLASK_APP=server deploy: resources: @@ -177,7 +175,6 @@ services: dockerfile: ./services/openai_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8159 environment: - - CUDA_VISIBLE_DEVICES=0 - FLASK_APP=server deploy: resources: @@ -197,7 +194,6 @@ services: dockerfile: ./services/openai_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8160 environment: - - CUDA_VISIBLE_DEVICES=0 - FLASK_APP=server deploy: resources: @@ -217,7 +213,44 @@ services: dockerfile: ./services/openai_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8167 environment: - - CUDA_VISIBLE_DEVICES=0 + - FLASK_APP=server + deploy: + resources: + limits: + memory: 100M + reservations: + memory: 100M + + anthropic-api-claude-v1: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8164 + SERVICE_NAME: anthropic_api_claude_v1 + PRETRAINED_MODEL_NAME_OR_PATH: claude-1 + context: . + dockerfile: ./services/anthropic_api_lm/Dockerfile + command: flask run -h 0.0.0.0 -p 8164 + environment: + - FLASK_APP=server + deploy: + resources: + limits: + memory: 100M + reservations: + memory: 100M + + anthropic-api-claude-instant-v1: + env_file: [ .env ] + build: + args: + SERVICE_PORT: 8163 + SERVICE_NAME: anthropic_api_claude_instant_v1 + PRETRAINED_MODEL_NAME_OR_PATH: claude-instant-1 + context: . + dockerfile: ./services/anthropic_api_lm/Dockerfile + command: flask run -h 0.0.0.0 -p 8163 + environment: - FLASK_APP=server deploy: resources: diff --git a/common/generative_configs/empty_generative_config.json b/common/generative_configs/empty_generative_config.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/common/generative_configs/empty_generative_config.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/components.tsv b/components.tsv index 789953d85f..5e217d76e6 100644 --- a/components.tsv +++ b/components.tsv @@ -164,8 +164,8 @@ 8160 openai-api-gpt4-32k 8161 8162 dff-google-api-skill -8163 anthropic-api-claudev13 -8164 anthropic-api-claudev1 +8163 anthropic-api-claude-instant-v1 +8164 anthropic-api-claude-v1 8165 8166 8167 openai-api-chatgpt-16k diff --git a/components/lkfjsdkur348rnjlkadfg12esdf.yml b/components/lkfjsdkur348rnjlkadfg12esdf.yml index b02a7769b0..bbad98b970 100644 --- a/components/lkfjsdkur348rnjlkadfg12esdf.yml +++ b/components/lkfjsdkur348rnjlkadfg12esdf.yml @@ -1,4 +1,4 @@ -name: anthropic_api_claudev1 +name: anthropic_api_claude_v1 display_name: Anthropic Claude v1 component_type: Generative model_type: NN-based @@ -16,7 +16,7 @@ group: services connector: protocol: http timeout: 120.0 - url: http://anthropic-api-claudev1:8164/respond + url: http://anthropic-api-claude-v1:8164/respond dialog_formatter: null response_formatter: null previous_services: null @@ -24,5 +24,5 @@ required_previous_services: null state_manager_method: null tags: null endpoint: respond -service: services/anthropic_api_lm/service_configs/anthropic-api-claudev1 +service: services/anthropic_api_lm/service_configs/anthropic-api-claude-v1 date_created: '2023-06-01T09:45:32' diff --git a/components/njkhf834hfbg78sf.yml b/components/njkhf834hfbg78sf.yml index 03f8be21ae..61fadedd92 100644 --- a/components/njkhf834hfbg78sf.yml +++ b/components/njkhf834hfbg78sf.yml @@ -1,4 +1,4 @@ -name: anthropic_api_claudev13 +name: anthropic_api_claude_instant_v1 display_name: Anthropic Claude v1.3 component_type: Generative model_type: NN-based @@ -17,7 +17,7 @@ group: services connector: protocol: http timeout: 120.0 - url: http://anthropic-api-claudev13:8163/respond + url: http://anthropic-api-claude-instant-v1:8163/respond dialog_formatter: null response_formatter: null previous_services: null @@ -25,5 +25,5 @@ required_previous_services: null state_manager_method: null tags: null endpoint: respond -service: services/anthropic_api_lm/service_configs/anthropic-api-claudev13 +service: services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1 date_created: '2023-06-01T09:45:32' diff --git a/services/anthropic_api_lm/server.py b/services/anthropic_api_lm/server.py index eaa24d5abb..8b430d0fbc 100644 --- a/services/anthropic_api_lm/server.py +++ b/services/anthropic_api_lm/server.py @@ -24,8 +24,8 @@ app = Flask(__name__) logging.getLogger("werkzeug").setLevel("WARNING") DEFAULT_CONFIGS = { - "claudev1": json.load(open("common/generative_configs/default_generative_config.json", "r")), - "claudev13": json.load(open("common/generative_configs/default_generative_config.json", "r")), + "claude-1": json.load(open("common/generative_configs/empty_generative_config.json", "r")), + "claude-instant-1": json.load(open("common/generative_configs/empty_generative_config.json", "r")), } diff --git a/services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/environment.yml b/services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/environment.yml new file mode 100644 index 0000000000..2ae700542d --- /dev/null +++ b/services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/environment.yml @@ -0,0 +1,4 @@ +SERVICE_PORT: 8163 +SERVICE_NAME: anthropic_api_claude_instant_v1 +PRETRAINED_MODEL_NAME_OR_PATH: claude-instant-1 +FLASK_APP: server diff --git a/services/anthropic_api_lm/service_configs/anthropic-api-claudev13/service.yml b/services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/service.yml similarity index 72% rename from services/anthropic_api_lm/service_configs/anthropic-api-claudev13/service.yml rename to services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/service.yml index d5ff72c117..f34a8c8dcf 100644 --- a/services/anthropic_api_lm/service_configs/anthropic-api-claudev13/service.yml +++ b/services/anthropic_api_lm/service_configs/anthropic-api-claude-instant-v1/service.yml @@ -1,4 +1,4 @@ -name: anthropic-api-claudev13 +name: anthropic-api-claude-instant-v1 endpoints: - respond - generate_goals @@ -8,15 +8,13 @@ compose: build: args: SERVICE_PORT: 8163 - SERVICE_NAME: anthropic_api_claudev13 - PRETRAINED_MODEL_NAME_OR_PATH: claude-v1.2 - CUDA_VISIBLE_DEVICES: '0' + SERVICE_NAME: anthropic_api_claude_instant_v1 + PRETRAINED_MODEL_NAME_OR_PATH: claude-instant-1 FLASK_APP: server context: . dockerfile: ./services/anthropic_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8163 environment: - - CUDA_VISIBLE_DEVICES=0 - FLASK_APP=server deploy: resources: diff --git a/services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/environment.yml b/services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/environment.yml new file mode 100644 index 0000000000..6d94598d9b --- /dev/null +++ b/services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/environment.yml @@ -0,0 +1,4 @@ +SERVICE_PORT: 8164 +SERVICE_NAME: anthropic_api_claude_v1 +PRETRAINED_MODEL_NAME_OR_PATH: claude-1 +FLASK_APP: server diff --git a/services/anthropic_api_lm/service_configs/anthropic-api-claudev1/service.yml b/services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/service.yml similarity index 73% rename from services/anthropic_api_lm/service_configs/anthropic-api-claudev1/service.yml rename to services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/service.yml index 8b806ba5ba..27085a289f 100644 --- a/services/anthropic_api_lm/service_configs/anthropic-api-claudev1/service.yml +++ b/services/anthropic_api_lm/service_configs/anthropic-api-claude-v1/service.yml @@ -1,4 +1,4 @@ -name: anthropic-api-claudev1 +name: anthropic-api-claude-v1 endpoints: - respond - generate_goals @@ -8,15 +8,13 @@ compose: build: args: SERVICE_PORT: 8164 - SERVICE_NAME: anthropic_api_claudev1 - PRETRAINED_MODEL_NAME_OR_PATH: claude-v1 - CUDA_VISIBLE_DEVICES: '0' + SERVICE_NAME: anthropic_api_claude_v1 + PRETRAINED_MODEL_NAME_OR_PATH: claude-1 FLASK_APP: server context: . dockerfile: ./services/anthropic_api_lm/Dockerfile command: flask run -h 0.0.0.0 -p 8164 environment: - - CUDA_VISIBLE_DEVICES=0 - FLASK_APP=server deploy: resources: diff --git a/services/anthropic_api_lm/service_configs/anthropic-api-claudev1/environment.yml b/services/anthropic_api_lm/service_configs/anthropic-api-claudev1/environment.yml deleted file mode 100644 index 96a7ba31a8..0000000000 --- a/services/anthropic_api_lm/service_configs/anthropic-api-claudev1/environment.yml +++ /dev/null @@ -1,5 +0,0 @@ -SERVICE_PORT: 8164 -SERVICE_NAME: anthropic_api_claudev1 -PRETRAINED_MODEL_NAME_OR_PATH: claude-v1 -CUDA_VISIBLE_DEVICES: '0' -FLASK_APP: server diff --git a/services/anthropic_api_lm/service_configs/anthropic-api-claudev13/environment.yml b/services/anthropic_api_lm/service_configs/anthropic-api-claudev13/environment.yml deleted file mode 100644 index 24366661f7..0000000000 --- a/services/anthropic_api_lm/service_configs/anthropic-api-claudev13/environment.yml +++ /dev/null @@ -1,5 +0,0 @@ -SERVICE_PORT: 8163 -SERVICE_NAME: anthropic_api_claudev13 -PRETRAINED_MODEL_NAME_OR_PATH: claude-v1.2 -CUDA_VISIBLE_DEVICES: '0' -FLASK_APP: server diff --git a/skills/dff_universal_prompted_skill/scenario/response.py b/skills/dff_universal_prompted_skill/scenario/response.py index e78a015848..bb05b4814e 100644 --- a/skills/dff_universal_prompted_skill/scenario/response.py +++ b/skills/dff_universal_prompted_skill/scenario/response.py @@ -35,6 +35,8 @@ "http://openai-api-gpt4:8159/respond": ["OPENAI_API_KEY", "OPENAI_ORGANIZATION"], "http://openai-api-gpt4-32k:8160/respond": ["OPENAI_API_KEY", "OPENAI_ORGANIZATION"], "http://transformers-lm-gptjt:8161/respond": [], + "http://anthropic-api-claude-v1:8164/respond": ["ANTHROPIC_API_KEY"], + "http://anthropic-api-claude-instant-v1:8163/respond": ["ANTHROPIC_API_KEY"], } From 23f97023382d75d53d08fe266ef8e70735890a16 Mon Sep 17 00:00:00 2001 From: dilyararimovna Date: Tue, 27 Jun 2023 15:07:49 +0300 Subject: [PATCH 08/11] fix: claude display name --- MODELS.md | 26 +++++++++++++------------- components/njkhf834hfbg78sf.yml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/MODELS.md b/MODELS.md index 89e06290c4..e18ce4e399 100644 --- a/MODELS.md +++ b/MODELS.md @@ -2,16 +2,16 @@ Here you may find a list of models that currently available for use in Generative Assistants. -| model name | container name | model link | open-source? | size (billion parameters) | GPU usage | max tokens (prompt + response) | description | -|---------------------------|---------------------------------|-------------------------------------------------------------------------|--------------------------|---------------------------|---------------------------|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| BLOOMZ 7B | transformers-lm-bloomz7b | [link](https://huggingface.co/bigscience/bloomz-7b1) | yes | 7.1B | 33GB | 2,048 tokens | An open-source multilingual instruction-based large language model (46 languages). NB: free of charge. This model is up and running on our servers and can be used for free. | -| GPT-J 6B | transformers-lm-gptj | [link](https://huggingface.co/EleutherAI/gpt-j-6b) | yes | 6B | 25GB | 2,048 tokens | An open-source English-only large language model which is NOT fine-tuned for instruction following and NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | -| GPT-3.5 | openai-api-davinci3 | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,097 tokens | A multulingual instruction-based large language model which is capable of code generation. Unlike ChatGPT, not optimised for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| ChatGPT | openai-api-chatgpt | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,096 tokens | Based on gpt-3.5-turbo -- the most capable of the entire GPT-3/GPT-3.5 models family. Optimized for chat. Able to understand and generate code. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| Open-Assistant Pythia 12B | transformers-lm-oasst12b | [link](https://huggingface.co/OpenAssistant/pythia-12b-sft-v8-7k-steps) | yes | 12B | 26GB (half-precision) | 5,120 tokens | An open-source English-only instruction-based large language model which is NOT good at answering math and coding questions. NB: free of charge. This model is up and running on our servers and can be used for free. | -| GPT-4 | openai-api-gpt4 | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 8,192 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. More capable than any GPT-3.5 model, able to do more complex tasks, and optimized for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| GPT-4 32K | openai-api-gpt4-32k | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 32,768 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. Same capabilities as the base gpt-4 mode but with 4x the context length. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| GPT-JT 6B | transformers-lm-gptjt | [link](https://huggingface.co/togethercomputer/GPT-JT-6B-v1) | yes | 6B | 26GB | 2,048 tokens | An open-source English-only large language model which was fine-tuned for instruction following but is NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | -| ChatGPT 16k | openai-api-chatgpt-16k | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 16,384 tokens | Same capabilities as the standard gpt-3.5-turbo model but with 4 times the context. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | -| Anthropic Claude-v1 | anthropic-api-claude-v1 | [link](https://docs.anthropic.com/claude/reference/complete_post) | no (paid access via API) | | - (cannot be run locally) | 9,000 tokens | The largest model, ideal for a wide range of more complex tasks. NB: paid. You must provide your Anthropic API key to use the model. Your Anthropic API account will be charged according to your usage. | -| Anthropic Claude-v1 | anthropic-api-claude-instant-v1 | [link](https://docs.anthropic.com/claude/reference/complete_post) | no (paid access via API) | | - (cannot be run locally) | 9,000 tokens | A smaller model with far lower latency, sampling at roughly 40 words/sec! Its output quality is somewhat lower than the latest claude-1 model, particularly for complex tasks. However, it is much less expensive and blazing fast. NB: paid. You must provide your Anthropic API key to use the model. Your Anthropic API account will be charged according to your usage. | +| model name | container name | model link | open-source? | size (billion parameters) | GPU usage | max tokens (prompt + response) | description | +|------------------------------|---------------------------------|-------------------------------------------------------------------------|--------------------------|---------------------------|---------------------------|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| BLOOMZ 7B | transformers-lm-bloomz7b | [link](https://huggingface.co/bigscience/bloomz-7b1) | yes | 7.1B | 33GB | 2,048 tokens | An open-source multilingual instruction-based large language model (46 languages). NB: free of charge. This model is up and running on our servers and can be used for free. | +| GPT-J 6B | transformers-lm-gptj | [link](https://huggingface.co/EleutherAI/gpt-j-6b) | yes | 6B | 25GB | 2,048 tokens | An open-source English-only large language model which is NOT fine-tuned for instruction following and NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | +| GPT-3.5 | openai-api-davinci3 | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,097 tokens | A multulingual instruction-based large language model which is capable of code generation. Unlike ChatGPT, not optimised for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| ChatGPT | openai-api-chatgpt | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 4,096 tokens | Based on gpt-3.5-turbo -- the most capable of the entire GPT-3/GPT-3.5 models family. Optimized for chat. Able to understand and generate code. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| Open-Assistant Pythia 12B | transformers-lm-oasst12b | [link](https://huggingface.co/OpenAssistant/pythia-12b-sft-v8-7k-steps) | yes | 12B | 26GB (half-precision) | 5,120 tokens | An open-source English-only instruction-based large language model which is NOT good at answering math and coding questions. NB: free of charge. This model is up and running on our servers and can be used for free. | +| GPT-4 | openai-api-gpt4 | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 8,192 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. More capable than any GPT-3.5 model, able to do more complex tasks, and optimized for chat. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| GPT-4 32K | openai-api-gpt4-32k | [link](https://platform.openai.com/docs/models/gpt-4) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 32,768 tokens | A multilingual instruction-based large language model which is capable of code generation and other complex tasks. Same capabilities as the base gpt-4 mode but with 4x the context length. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| GPT-JT 6B | transformers-lm-gptjt | [link](https://huggingface.co/togethercomputer/GPT-JT-6B-v1) | yes | 6B | 26GB | 2,048 tokens | An open-source English-only large language model which was fine-tuned for instruction following but is NOT capable of code generation. NB: free of charge. This model is up and running on our servers and can be used for free. | +| ChatGPT 16k | openai-api-chatgpt-16k | [link](https://platform.openai.com/docs/models/gpt-3-5) | no (paid access via API) | supposedly, 175B | - (cannot be run locally) | 16,384 tokens | Same capabilities as the standard gpt-3.5-turbo model but with 4 times the context. NB: paid. You must provide your OpenAI API key to use the model. Your OpenAI account will be charged according to your usage. | +| Anthropic Claude-v1 | anthropic-api-claude-v1 | [link](https://docs.anthropic.com/claude/reference/complete_post) | no (paid access via API) | | - (cannot be run locally) | 9,000 tokens | The largest model, ideal for a wide range of more complex tasks. NB: paid. You must provide your Anthropic API key to use the model. Your Anthropic API account will be charged according to your usage. | +| Anthropic Claude Instant v1 | anthropic-api-claude-instant-v1 | [link](https://docs.anthropic.com/claude/reference/complete_post) | no (paid access via API) | | - (cannot be run locally) | 9,000 tokens | A smaller model with far lower latency, sampling at roughly 40 words/sec! Its output quality is somewhat lower than the latest claude-1 model, particularly for complex tasks. However, it is much less expensive and blazing fast. NB: paid. You must provide your Anthropic API key to use the model. Your Anthropic API account will be charged according to your usage. | diff --git a/components/njkhf834hfbg78sf.yml b/components/njkhf834hfbg78sf.yml index 61fadedd92..b31ed20a7c 100644 --- a/components/njkhf834hfbg78sf.yml +++ b/components/njkhf834hfbg78sf.yml @@ -1,5 +1,5 @@ name: anthropic_api_claude_instant_v1 -display_name: Anthropic Claude v1.3 +display_name: Anthropic Claude Instant v1 component_type: Generative model_type: NN-based is_customizable: false From 24e159acdf749bdefb43de90db0505b60e2616ab Mon Sep 17 00:00:00 2001 From: Nika Smilga <42929200+smilni@users.noreply.github.com> Date: Tue, 27 Jun 2023 15:41:07 +0300 Subject: [PATCH 09/11] Fix/better llm response selector (#498) * moved prompt with task to the end * openass instead of chatgpt * longer context and slightly different prompt for transformers * chatgpt as default selector * revert proxy file * revert docker-compose file * removed configs from response selection * config fix --- common/generative_configs/generative_config_long.json | 8 ++++++++ .../generative_configs/default_generative_config.json | 8 -------- .../generative_configs/openai-chatgpt.json | 7 ------- .../generative_configs/openai-text-davinci-003-long.json | 7 ------- .../generative_configs/openai-text-davinci-003.json | 7 ------- response_selectors/llm_based_response_selector/server.py | 5 ++++- 6 files changed, 12 insertions(+), 30 deletions(-) create mode 100644 common/generative_configs/generative_config_long.json delete mode 100644 response_selectors/llm_based_response_selector/generative_configs/default_generative_config.json delete mode 100644 response_selectors/llm_based_response_selector/generative_configs/openai-chatgpt.json delete mode 100644 response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003-long.json delete mode 100644 response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003.json diff --git a/common/generative_configs/generative_config_long.json b/common/generative_configs/generative_config_long.json new file mode 100644 index 0000000000..b85abdab8b --- /dev/null +++ b/common/generative_configs/generative_config_long.json @@ -0,0 +1,8 @@ +{ + "max_new_tokens": 256, + "min_new_tokens": 8, + "top_p": 0.9, + "temperature": 0.9, + "do_sample": true, + "num_return_sequences": 2 +} \ No newline at end of file diff --git a/response_selectors/llm_based_response_selector/generative_configs/default_generative_config.json b/response_selectors/llm_based_response_selector/generative_configs/default_generative_config.json deleted file mode 100644 index 1edf84f186..0000000000 --- a/response_selectors/llm_based_response_selector/generative_configs/default_generative_config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "max_length": 60, - "min_length": 8, - "top_p": 0.9, - "temperature": 0.9, - "do_sample": true, - "num_return_sequences": 2 -} \ No newline at end of file diff --git a/response_selectors/llm_based_response_selector/generative_configs/openai-chatgpt.json b/response_selectors/llm_based_response_selector/generative_configs/openai-chatgpt.json deleted file mode 100644 index 107e944b80..0000000000 --- a/response_selectors/llm_based_response_selector/generative_configs/openai-chatgpt.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "max_tokens": 64, - "temperature": 0.4, - "top_p": 1.0, - "frequency_penalty": 0, - "presence_penalty": 0 -} \ No newline at end of file diff --git a/response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003-long.json b/response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003-long.json deleted file mode 100644 index a109e4db88..0000000000 --- a/response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003-long.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "max_tokens": 128, - "temperature": 0.4, - "top_p": 1.0, - "frequency_penalty": 0, - "presence_penalty": 0 -} \ No newline at end of file diff --git a/response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003.json b/response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003.json deleted file mode 100644 index 107e944b80..0000000000 --- a/response_selectors/llm_based_response_selector/generative_configs/openai-text-davinci-003.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "max_tokens": 64, - "temperature": 0.4, - "top_p": 1.0, - "frequency_penalty": 0, - "presence_penalty": 0 -} \ No newline at end of file diff --git a/response_selectors/llm_based_response_selector/server.py b/response_selectors/llm_based_response_selector/server.py index bc7fc64012..ab30ca81b5 100644 --- a/response_selectors/llm_based_response_selector/server.py +++ b/response_selectors/llm_based_response_selector/server.py @@ -58,7 +58,10 @@ def select_response_by_scores(hypotheses, scores): def select_response(dialog_context, hypotheses, human_uttr_attributes): try: - curr_prompt = PROMPT + "\nHypotheses:\n" + "\n".join([f'"{hyp["text"]}"' for hyp in hypotheses]) + if "transformers" in GENERATIVE_SERVICE_URL: + curr_prompt = "Hypotheses:\n" + "\n".join([f'"{hyp["text"]}"' for hyp in hypotheses]) + "\n" + PROMPT + else: + curr_prompt = PROMPT + "\nHypotheses:\n" + "\n".join([f'"{hyp["text"]}"' for hyp in hypotheses]) logger.info(f"llm_based_response_selector sends dialog context to llm:\n`{dialog_context}`") logger.info(f"llm_based_response_selector sends prompt to llm:\n`{curr_prompt}`") From 29bbd05fbe4b5168f82d8ad639c3d5985c3f6f4c Mon Sep 17 00:00:00 2001 From: "Dilyara Zharikova (Baymurzina)" Date: Wed, 28 Jun 2023 15:01:19 +0300 Subject: [PATCH 10/11] Feat/newer python dockerfile (#502) * fix: use newer python * fix: use newer python * fix: numpy version --- .../confidence_based_response_selector/Dockerfile | 2 +- .../confidence_based_response_selector/requirements.txt | 2 +- response_selectors/llm_based_response_selector/Dockerfile | 2 +- response_selectors/llm_based_response_selector/requirements.txt | 2 +- response_selectors/ranking_based_response_selector/Dockerfile | 2 +- .../ranking_based_response_selector/requirements.txt | 2 +- services/anthropic_api_lm/Dockerfile | 2 +- services/openai_api_lm/Dockerfile | 2 +- skill_selectors/rule_based_selector/Dockerfile | 2 +- skills/oscar_skill/Dockerfile | 2 +- skills/oscar_skill/requirements.txt | 2 +- skills/program_y_deepy/Dockerfile | 2 +- skills/superbowl_skill/Dockerfile | 2 +- skills/superbowl_skill/requirements.txt | 2 +- skills/topicalchat_tfidf_retrieval/Dockerfile | 2 +- skills/topicalchat_tfidf_retrieval/requirements.txt | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/response_selectors/confidence_based_response_selector/Dockerfile b/response_selectors/confidence_based_response_selector/Dockerfile index cda20638a2..0d2325b6ef 100644 --- a/response_selectors/confidence_based_response_selector/Dockerfile +++ b/response_selectors/confidence_based_response_selector/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 WORKDIR /src diff --git a/response_selectors/confidence_based_response_selector/requirements.txt b/response_selectors/confidence_based_response_selector/requirements.txt index 1d2e7fb9f2..0261e2b1f2 100644 --- a/response_selectors/confidence_based_response_selector/requirements.txt +++ b/response_selectors/confidence_based_response_selector/requirements.txt @@ -2,7 +2,7 @@ flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 requests==2.22.0 -numpy==1.17.2 +numpy==1.25.0 sentry-sdk==0.12.3 jinja2<=3.0.3 Werkzeug<=2.0.3 diff --git a/response_selectors/llm_based_response_selector/Dockerfile b/response_selectors/llm_based_response_selector/Dockerfile index 252715eff7..8b6bc827e9 100644 --- a/response_selectors/llm_based_response_selector/Dockerfile +++ b/response_selectors/llm_based_response_selector/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 RUN mkdir /src diff --git a/response_selectors/llm_based_response_selector/requirements.txt b/response_selectors/llm_based_response_selector/requirements.txt index 1d2e7fb9f2..0261e2b1f2 100644 --- a/response_selectors/llm_based_response_selector/requirements.txt +++ b/response_selectors/llm_based_response_selector/requirements.txt @@ -2,7 +2,7 @@ flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 requests==2.22.0 -numpy==1.17.2 +numpy==1.25.0 sentry-sdk==0.12.3 jinja2<=3.0.3 Werkzeug<=2.0.3 diff --git a/response_selectors/ranking_based_response_selector/Dockerfile b/response_selectors/ranking_based_response_selector/Dockerfile index cf1753c468..67666b5916 100644 --- a/response_selectors/ranking_based_response_selector/Dockerfile +++ b/response_selectors/ranking_based_response_selector/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 RUN mkdir /src diff --git a/response_selectors/ranking_based_response_selector/requirements.txt b/response_selectors/ranking_based_response_selector/requirements.txt index 1d2e7fb9f2..0261e2b1f2 100644 --- a/response_selectors/ranking_based_response_selector/requirements.txt +++ b/response_selectors/ranking_based_response_selector/requirements.txt @@ -2,7 +2,7 @@ flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 requests==2.22.0 -numpy==1.17.2 +numpy==1.25.0 sentry-sdk==0.12.3 jinja2<=3.0.3 Werkzeug<=2.0.3 diff --git a/services/anthropic_api_lm/Dockerfile b/services/anthropic_api_lm/Dockerfile index f2241b109b..8d66fe5bc8 100644 --- a/services/anthropic_api_lm/Dockerfile +++ b/services/anthropic_api_lm/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 WORKDIR /src diff --git a/services/openai_api_lm/Dockerfile b/services/openai_api_lm/Dockerfile index ece445075e..bfbb08a12f 100644 --- a/services/openai_api_lm/Dockerfile +++ b/services/openai_api_lm/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 WORKDIR /src diff --git a/skill_selectors/rule_based_selector/Dockerfile b/skill_selectors/rule_based_selector/Dockerfile index c96ff0dce1..fc924a6f36 100644 --- a/skill_selectors/rule_based_selector/Dockerfile +++ b/skill_selectors/rule_based_selector/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 RUN mkdir /src /src/common diff --git a/skills/oscar_skill/Dockerfile b/skills/oscar_skill/Dockerfile index 2e3a3c2b4e..82bcc14372 100644 --- a/skills/oscar_skill/Dockerfile +++ b/skills/oscar_skill/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 WORKDIR /src diff --git a/skills/oscar_skill/requirements.txt b/skills/oscar_skill/requirements.txt index 1d2e7fb9f2..0261e2b1f2 100644 --- a/skills/oscar_skill/requirements.txt +++ b/skills/oscar_skill/requirements.txt @@ -2,7 +2,7 @@ flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 requests==2.22.0 -numpy==1.17.2 +numpy==1.25.0 sentry-sdk==0.12.3 jinja2<=3.0.3 Werkzeug<=2.0.3 diff --git a/skills/program_y_deepy/Dockerfile b/skills/program_y_deepy/Dockerfile index 4bdf6c61cc..c3f63b3756 100644 --- a/skills/program_y_deepy/Dockerfile +++ b/skills/program_y_deepy/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 WORKDIR /app diff --git a/skills/superbowl_skill/Dockerfile b/skills/superbowl_skill/Dockerfile index 2e3a3c2b4e..82bcc14372 100644 --- a/skills/superbowl_skill/Dockerfile +++ b/skills/superbowl_skill/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 WORKDIR /src diff --git a/skills/superbowl_skill/requirements.txt b/skills/superbowl_skill/requirements.txt index 1d2e7fb9f2..0261e2b1f2 100644 --- a/skills/superbowl_skill/requirements.txt +++ b/skills/superbowl_skill/requirements.txt @@ -2,7 +2,7 @@ flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 requests==2.22.0 -numpy==1.17.2 +numpy==1.25.0 sentry-sdk==0.12.3 jinja2<=3.0.3 Werkzeug<=2.0.3 diff --git a/skills/topicalchat_tfidf_retrieval/Dockerfile b/skills/topicalchat_tfidf_retrieval/Dockerfile index d803e92202..ca99dae01a 100644 --- a/skills/topicalchat_tfidf_retrieval/Dockerfile +++ b/skills/topicalchat_tfidf_retrieval/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.4 +FROM python:3.10 RUN mkdir /src COPY ./requirements.txt /src/requirements.txt diff --git a/skills/topicalchat_tfidf_retrieval/requirements.txt b/skills/topicalchat_tfidf_retrieval/requirements.txt index 1c10f16aa9..19a456cee5 100644 --- a/skills/topicalchat_tfidf_retrieval/requirements.txt +++ b/skills/topicalchat_tfidf_retrieval/requirements.txt @@ -2,7 +2,7 @@ flask==1.1.1 itsdangerous==2.0.1 gunicorn==19.9.0 requests==2.22.0 -numpy==1.17.2 +numpy==1.25.0 sentry-sdk==0.13.3 scikit-learn==0.21.3 Cython==0.29.13 From e0bf64eb785babbbe4716fc65bb347576ac0518a Mon Sep 17 00:00:00 2001 From: Fedor Ignatov Date: Wed, 28 Jun 2023 15:34:44 +0300 Subject: [PATCH 11/11] refactor: openai limit increased from 100 to 500M (#503) --- .../deeppavlov_assistant/docker-compose.override.yml | 2 +- .../deepy_assistant/docker-compose.override.yml | 2 +- .../docker-compose.override.yml | 2 +- .../fairytale_assistant/docker-compose.override.yml | 2 +- .../docker-compose.override.yml | 2 +- .../docker-compose.override.yml | 2 +- .../docker-compose.override.yml | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/assistant_dists/deeppavlov_assistant/docker-compose.override.yml b/assistant_dists/deeppavlov_assistant/docker-compose.override.yml index bb970097ef..786f12072e 100644 --- a/assistant_dists/deeppavlov_assistant/docker-compose.override.yml +++ b/assistant_dists/deeppavlov_assistant/docker-compose.override.yml @@ -88,7 +88,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M diff --git a/assistant_dists/deepy_assistant/docker-compose.override.yml b/assistant_dists/deepy_assistant/docker-compose.override.yml index fb151aa0a6..992fdf0cfe 100644 --- a/assistant_dists/deepy_assistant/docker-compose.override.yml +++ b/assistant_dists/deepy_assistant/docker-compose.override.yml @@ -89,7 +89,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M diff --git a/assistant_dists/dream_persona_openai_prompted/docker-compose.override.yml b/assistant_dists/dream_persona_openai_prompted/docker-compose.override.yml index b6ec4c5b1c..bb3fbacfb7 100644 --- a/assistant_dists/dream_persona_openai_prompted/docker-compose.override.yml +++ b/assistant_dists/dream_persona_openai_prompted/docker-compose.override.yml @@ -122,7 +122,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M diff --git a/assistant_dists/fairytale_assistant/docker-compose.override.yml b/assistant_dists/fairytale_assistant/docker-compose.override.yml index 2358607e4c..084ffe41a1 100644 --- a/assistant_dists/fairytale_assistant/docker-compose.override.yml +++ b/assistant_dists/fairytale_assistant/docker-compose.override.yml @@ -122,7 +122,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M diff --git a/assistant_dists/life_coaching_assistant/docker-compose.override.yml b/assistant_dists/life_coaching_assistant/docker-compose.override.yml index 88994175ae..0933d7587b 100644 --- a/assistant_dists/life_coaching_assistant/docker-compose.override.yml +++ b/assistant_dists/life_coaching_assistant/docker-compose.override.yml @@ -122,7 +122,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M diff --git a/assistant_dists/multiskill_ai_assistant/docker-compose.override.yml b/assistant_dists/multiskill_ai_assistant/docker-compose.override.yml index 5d194767b5..e92b51acba 100644 --- a/assistant_dists/multiskill_ai_assistant/docker-compose.override.yml +++ b/assistant_dists/multiskill_ai_assistant/docker-compose.override.yml @@ -126,7 +126,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M diff --git a/assistant_dists/universal_prompted_assistant/docker-compose.override.yml b/assistant_dists/universal_prompted_assistant/docker-compose.override.yml index 0c2fd45ad6..48400c8137 100644 --- a/assistant_dists/universal_prompted_assistant/docker-compose.override.yml +++ b/assistant_dists/universal_prompted_assistant/docker-compose.override.yml @@ -141,7 +141,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M @@ -160,7 +160,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M @@ -179,7 +179,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M @@ -198,7 +198,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M @@ -217,7 +217,7 @@ services: deploy: resources: limits: - memory: 100M + memory: 500M reservations: memory: 100M