def predict_action_probabilities(self, tracker: DialogueStateTracker, domain: Domain) -> List[float]: """Predicts the corresponding form action if there is an active form""" result = [0.0] * domain.num_actions if tracker.active_form.get('name'): logger.debug("There is an active form '{}'" "".format(tracker.active_form['name'])) if tracker.latest_action_name == ACTION_LISTEN_NAME: # predict form action after user utterance if tracker.active_form.get('rejected'): if self.state_is_unhappy(tracker, domain): tracker.update(FormValidation(False)) return result idx = domain.index_for_action(tracker.active_form['name']) result[idx] = FORM_SCORE elif tracker.latest_action_name == tracker.active_form.get('name'): # predict action_listen after form action idx = domain.index_for_action(ACTION_LISTEN_NAME) result[idx] = FORM_SCORE else: logger.debug("There is no active form") return result
def predict_action_probabilities(self, tracker: DialogueStateTracker, domain: Domain) -> List[float]: """Predicts the assigned action. If the current intent is assigned to an action that action will be predicted with the highest probability of all policies. If it is not the policy will predict zero for every action.""" prediction = [0.0] * domain.num_actions intent = tracker.latest_message.intent.get('name') action = domain.intent_properties.get(intent, {}).get('triggers') if tracker.latest_action_name == ACTION_LISTEN_NAME: if action: idx = domain.index_for_action(action) if idx is None: logger.warning("MappingPolicy tried to predict unkown " "action '{}'.".format(action)) else: prediction[idx] = 1 elif intent == USER_INTENT_RESTART: idx = domain.index_for_action(ACTION_RESTART_NAME) prediction[idx] = 1 elif intent == USER_INTENT_BACK: idx = domain.index_for_action(ACTION_BACK_NAME) prediction[idx] = 1 elif tracker.latest_action_name == action and action is not None: latest_action = tracker.get_last_event_for(ActionExecuted) assert latest_action.action_name == action if latest_action.policy == type(self).__name__: # this ensures that we only predict listen, if we predicted # the mapped action idx = domain.index_for_action(ACTION_LISTEN_NAME) prediction[idx] = 1 return prediction
def _write_domain_to_file(domain_path: Text, evts: List[Dict[Text, Any]], endpoint: EndpointConfig) -> None: """Write an updated domain file to the file path.""" domain = retrieve_domain(endpoint) old_domain = Domain.from_dict(domain) messages = _collect_messages(evts) actions = _collect_actions(evts) domain_dict = dict.fromkeys(domain.keys(), []) # TODO for now there is no way to distinguish between action and form domain_dict["forms"] = [] domain_dict["intents"] = _intents_from_messages(messages) domain_dict["entities"] = _entities_from_messages(messages) # do not automatically add default actions to the domain dict domain_dict["actions"] = list({ e["name"] for e in actions if e["name"] not in default_action_names() }) new_domain = Domain.from_dict(domain_dict) old_domain.merge(new_domain).persist_clean(domain_path)
def _write_domain_to_file( domain_path: Text, evts: List[Dict[Text, Any]], endpoint: EndpointConfig ) -> None: """Write an updated domain file to the file path.""" domain = retrieve_domain(endpoint) old_domain = Domain.from_dict(domain) messages = _collect_messages(evts) actions = _collect_actions(evts) # TODO for now there is no way to distinguish between action and form intent_properties = Domain.collect_intent_properties( _intents_from_messages(messages)) collected_actions = list({e["name"] for e in actions if e["name"] not in default_action_names()}) new_domain = Domain( intent_properties=intent_properties, entities=_entities_from_messages(messages), slots=[], templates={}, action_names=collected_actions, form_names=[]) old_domain.merge(new_domain).persist_clean(domain_path)
def probabilities_using_best_policy(self, tracker: DialogueStateTracker, domain: Domain ) -> Tuple[List[float], Text]: result = None max_confidence = -1 best_policy_name = None best_policy_priority = -1 for i, p in enumerate(self.policies): probabilities = p.predict_action_probabilities(tracker, domain) if isinstance(tracker.events[-1], ActionExecutionRejected): probabilities[domain.index_for_action( tracker.events[-1].action_name)] = 0.0 confidence = np.max(probabilities) if (confidence, p.priority) > (max_confidence, best_policy_priority): max_confidence = confidence result = probabilities best_policy_name = 'policy_{}_{}'.format(i, type(p).__name__) best_policy_priority = p.priority if (result.index(max_confidence) == domain.index_for_action(ACTION_LISTEN_NAME) and tracker.latest_action_name == ACTION_LISTEN_NAME and self.is_not_memo_policy(best_policy_name)): # Trigger the fallback policy when ActionListen is predicted after # a user utterance. This is done on the condition that: # - a fallback policy is present, # - there was just a user message and the predicted # action is action_listen by a policy # other than the MemoizationPolicy fallback_idx_policy = [(i, p) for i, p in enumerate(self.policies) if isinstance(p, FallbackPolicy)] if fallback_idx_policy: fallback_idx, fallback_policy = fallback_idx_policy[0] logger.debug("Action 'action_listen' was predicted after " "a user message using {}. " "Predicting fallback action: {}" "".format(best_policy_name, fallback_policy.fallback_action_name)) result = fallback_policy.fallback_scores(domain) best_policy_name = 'policy_{}_{}'.format( fallback_idx, type(fallback_policy).__name__) # normalize probabilities if np.sum(result) != 0: result = result / np.nansum(result) logger.debug("Predicted next action using {}" "".format(best_policy_name)) return result, best_policy_name
def test_custom_slot_type(tmpdir): domain_path = utilities.write_text_to_file(tmpdir, "domain.yml", """ slots: custom: type: tests.conftest.CustomSlot templates: utter_greet: - hey there! actions: - utter_greet """) Domain.load(domain_path)
def test_domain_fails_on_unknown_custom_slot_type(tmpdir): domain_path = utilities.write_text_to_file(tmpdir, "domain.yml", """ slots: custom: type: tests.conftest.Unknown templates: utter_greet: - hey there! actions: - utter_greet""") with pytest.raises(ValueError): Domain.load(domain_path)
def test_domain_action_instantiation(): domain = Domain(intent_properties={}, entities=[], slots=[], templates={}, action_names=["my_module.ActionTest", "utter_test"]) instantiated_actions = domain.actions(None) assert len(instantiated_actions) == 5 assert instantiated_actions[0].name() == "action_listen" assert instantiated_actions[1].name() == "action_restart" assert instantiated_actions[2].name() == "action_default_fallback" assert instantiated_actions[3].name() == "my_module.ActionTest" assert instantiated_actions[4].name() == "utter_test"
def test_merge_yaml_domains(): test_yaml_1 = """actions: - utter_greet config: store_entities_as_slots: true entities: [] intents: [] slots: {} templates: utter_greet: - text: hey there!""" test_yaml_2 = """actions: - utter_greet - utter_goodbye config: store_entities_as_slots: false entities: - cuisine intents: - greet slots: cuisine: type: text templates: utter_greet: - text: hey you!""" domain_1 = Domain.from_yaml(test_yaml_1) domain_2 = Domain.from_yaml(test_yaml_2) domain = domain_1.merge(domain_2) # single attribute should be taken from domain_1 assert domain.store_entities_as_slots # conflicts should be taken from domain_1 assert domain.templates == {"utter_greet": [{"text": "hey there!"}]} # lists should be deduplicated and merged assert domain.intents == ["greet"] assert domain.entities == ["cuisine"] assert isinstance(domain.slots[0], TextSlot) assert domain.slots[0].name == "cuisine" assert sorted(domain.user_actions) == sorted( ["utter_greet", "utter_goodbye"]) domain = domain_1.merge(domain_2, override=True) # single attribute should be taken from domain_2 assert not domain.store_entities_as_slots # conflicts should take value from domain_2 assert domain.templates == {"utter_greet": [{"text": "hey you!"}]}
def trained_policy(self, featurizer, priority): default_domain = Domain.load(DEFAULT_DOMAIN_PATH) policy = self.create_policy(featurizer, priority) training_trackers = train_trackers(default_domain, augmentation_factor=20) policy.train(training_trackers, default_domain) return policy
def __init__(self, log): self.logger = log directorioNLU = 'model/default/Jarvis' directorioDialogo = 'model/dialogue' if (os.path.isdir(directorioNLU)): self.interpreter = RasaNLUInterpreter( model_directory=directorioNLU) if (os.path.isdir(directorioDialogo)): with open("config/endpoint.yml", 'r') as stream: try: config = yaml.safe_load(stream) except yaml.YAMLError as exc: print(exc) action_endopoint = EndpointConfig( url=config["action_endpoint"]["url"]) tracker_store = MongoTrackerStore( domain=Domain.load('model/dialogue/domain.yml'), host=config["tracker_store"]["url"], db=config["tracker_store"]["db"], username=config["tracker_store"]["username"], password=config["tracker_store"]["password"]) self.agent = Agent.load(directorioDialogo, interpreter=self.interpreter, action_endpoint=action_endopoint, tracker_store=tracker_store) self._slots = {}
def test_tracker_store(store, pair): filename, domainpath = pair domain = Domain.load(domainpath) tracker = tracker_from_dialogue_file(filename, domain) store.save(tracker) restored = store.retrieve(tracker.sender_id) assert restored == tracker
def _load_and_set_updated_model(agent: 'Agent', model_directory: Text, fingerprint: Text): """Load the persisted model into memory and set the model on the agent.""" logger.debug("Found new model with fingerprint {}. Loading..." "".format(fingerprint)) stack_model_directory = _get_stack_model_directory(model_directory) if stack_model_directory: from rasa_core.interpreter import RasaNLUInterpreter nlu_model = os.path.join(stack_model_directory, "nlu") core_model = os.path.join(stack_model_directory, "core") interpreter = RasaNLUInterpreter(model_directory=nlu_model) else: interpreter = agent.interpreter core_model = model_directory domain_path = os.path.join(os.path.abspath(core_model), "domain.yml") domain = Domain.load(domain_path) # noinspection PyBroadException try: policy_ensemble = PolicyEnsemble.load(core_model) agent.update_model(domain, policy_ensemble, fingerprint, interpreter) logger.debug("Finished updating agent to new model.") except Exception: logger.exception("Failed to load policy and update agent. " "The previous model will stay loaded instead.")
def test_inmemory_tracker_store(filename): domain = Domain.load("data/test_domains/default.yml") tracker = tracker_from_dialogue_file(filename, domain) tracker_store = InMemoryTrackerStore(domain) tracker_store.save(tracker) restored = tracker_store.retrieve(tracker.sender_id) assert restored == tracker
def action_as_one_hot(action: Text, domain: Domain) -> np.ndarray: if action is None: return np.ones(domain.num_actions, dtype=int) * -1 y = np.zeros(domain.num_actions, dtype=int) y[domain.index_for_action(action)] = 1 return y
def explicit_events( self, domain: Domain, should_append_final_listen: bool = True) -> List[Event]: """Returns events contained in the story step including implicit events. Not all events are always listed in the story dsl. This includes listen actions as well as implicitly set slots. This functions makes these events explicit and returns them with the rest of the steps events.""" events = [] for e in self.events: if isinstance(e, UserUttered): self._add_action_listen(events) events.append(e) events.extend(domain.slots_for_entities(e.entities)) else: events.append(e) if not self.end_checkpoints and should_append_final_listen: self._add_action_listen(events) return events
async def load_model(request: Request): """Loads a zipped model, replacing the existing one.""" if 'model' not in request.files: # model file is missing raise ErrorResponse(400, "InvalidParameter", "You did not supply a model as part of your " "request.", {"parameter": "model", "in": "body"}) model_file = request.files['model'] logger.info("Received new model through REST interface.") zipped_path = tempfile.NamedTemporaryFile(delete=False, suffix=".zip") zipped_path.close() model_directory = tempfile.mkdtemp() model_file.save(zipped_path.name) logger.debug("Downloaded model to {}".format(zipped_path.name)) zip_ref = zipfile.ZipFile(zipped_path.name, 'r') zip_ref.extractall(model_directory) zip_ref.close() logger.debug("Unzipped model to {}".format( os.path.abspath(model_directory))) domain_path = os.path.join(os.path.abspath(model_directory), "domain.yml") domain = Domain.load(domain_path) ensemble = PolicyEnsemble.load(model_directory) app.agent.update_model(domain, ensemble, None) logger.debug("Finished loading new agent.") return response.text('', 204)
def test_domain_action_instantiation(): domain = Domain( intent_properties={}, entities=[], slots=[], templates={}, action_names=["my_module.ActionTest", "utter_test"]) instantiated_actions = domain.actions(None) assert len(instantiated_actions) == 5 assert instantiated_actions[0].name() == "action_listen" assert instantiated_actions[1].name() == "action_restart" assert instantiated_actions[2].name() == "action_default_fallback" assert instantiated_actions[3].name() == "my_module.ActionTest" assert instantiated_actions[4].name() == "utter_test"
def load(cls, path: Text, interpreter: Optional[NaturalLanguageInterpreter] = None, generator: Union[EndpointConfig, 'NLG'] = None, tracker_store: Optional['TrackerStore'] = None, action_endpoint: Optional[EndpointConfig] = None, ) -> 'Agent': """Load a persisted model from the passed path.""" if not path: raise ValueError("You need to provide a valid directory where " "to load the agent from when calling " "`Agent.load`.") if os.path.isfile(path): raise ValueError("You are trying to load a MODEL from a file " "('{}'), which is not possible. \n" "The persisted path should be a directory " "containing the various model files. \n\n" "If you want to load training data instead of " "a model, use `agent.load_data(...)` " "instead.".format(path)) domain = Domain.load(os.path.join(path, "domain.yml")) ensemble = PolicyEnsemble.load(path) if path else None # ensures the domain hasn't changed between test and train domain.compare_with_specification(path) return cls(domain=domain, policies=ensemble, interpreter=interpreter, generator=generator, tracker_store=tracker_store, action_endpoint=action_endpoint)
def test_domain_fails_on_duplicated_actions(): with pytest.raises(ValueError): Domain(intent_properties={}, entities=[], slots=[], templates={}, action_names=["random_name", "random_name"])
def predict_action_probabilities(self, tracker: DialogueStateTracker, domain: Domain) -> List[float]: """Predicts a fallback action if NLU confidence is low or no other policy has a high-confidence prediction""" nlu_data = tracker.latest_message.parse_data # if NLU interpreter does not provide confidence score, # it is set to 1.0 here in order # to not override standard behaviour nlu_confidence = nlu_data["intent"].get("confidence", 1.0) if tracker.latest_action_name == self.fallback_action_name: result = [0.0] * domain.num_actions idx = domain.index_for_action('action_listen') result[idx] = FALLBACK_SCORE elif self.should_fallback(nlu_confidence, tracker.latest_action_name): logger.debug("NLU confidence {} is lower " "than NLU threshold {}. " "Predicting fallback action: {}" "".format(nlu_confidence, self.nlu_threshold, self.fallback_action_name)) # we set this to 1.1 to make sure fallback overrides # the memoization policy result = self.fallback_scores(domain) else: # NLU confidence threshold is met, so # predict fallback action with confidence `core_threshold` # if this is the highest confidence in the ensemble, # the fallback action will be executed. result = self.fallback_scores(domain, self.core_threshold) return result
def test_domain_to_yaml(): test_yaml = """actions: - utter_greet config: store_entities_as_slots: true entities: [] intents: [] slots: {} templates: utter_greet: - text: hey there!""" domain = Domain.from_yaml(test_yaml) # python 3 and 2 are different here, python 3 will have a leading set # of --- at the beginning of the yml assert domain.as_yaml().strip().endswith(test_yaml.strip()) domain = Domain.from_yaml(domain.as_yaml())
def trained_policy(self, featurizer): default_domain = Domain.load(DEFAULT_DOMAIN_PATH) policy = self.create_policy(featurizer) training_trackers = train_trackers(default_domain) policy.train(training_trackers, default_domain, attn_before_rnn=True, attn_after_rnn=True) return policy
def test_inmemory_tracker_store(pair): filename, domainpath = pair domain = Domain.load(domainpath) tracker = tracker_from_dialogue_file(filename, domain) tracker_store = InMemoryTrackerStore(domain) tracker_store.save(tracker) restored = tracker_store.retrieve(tracker.sender_id) assert restored == tracker
def tracker_from_dialogue_file(filename: Text, domain: Domain = None): dialogue = read_dialogue_file(filename) if not domain: domain = Domain.load(DEFAULT_DOMAIN_PATH) tracker = DialogueStateTracker(dialogue.name, domain.slots) tracker.recreate_from_dialogue(dialogue) return tracker
def test_action(): domain = Domain.load('domain.yml') nlg = TemplatedNaturalLanguageGenerator(domain.templates) dispatcher = Dispatcher("my-sender", CollectingOutputChannel(), nlg) uid = str(uuid.uuid1()) tracker = DialogueStateTracker(uid, domain.slots) # print ("dispatcher,uid,tracker ===", dispatcher, uid, tracker) action = QuoraSearch() action.run(dispatcher, tracker, domain)
def test_utter_templates(): domain_file = "examples/moodbot/domain.yml" domain = Domain.load(domain_file) expected_template = { "text": "Hey! How are you?", "buttons": [{"title": "great", "payload": "great"}, {"title": "super sad", "payload": "super sad"}] } assert domain.random_template_for("utter_greet") == expected_template
def _write_domain_to_file(domain_path, evts, endpoint): # type: (Text, List[Dict[Text, Any]], EndpointConfig) -> None """Write an updated domain file to the file path.""" domain = retrieve_domain(endpoint) old_domain = Domain.from_dict(domain) messages = _collect_messages(evts) actions = _collect_actions(evts) domain_dict = dict.fromkeys(domain.keys(), {}) # type: Dict[Text, Any] domain_dict["intents"] = _intents_from_messages(messages) domain_dict["entities"] = _entities_from_messages(messages) domain_dict["actions"] = list({e["name"] for e in actions}) new_domain = Domain.from_dict(domain_dict) old_domain.merge(new_domain).persist_clean(domain_path)
def tracker_from_dialogue_file(filename, domain=None): dialogue = read_dialogue_file(filename) if domain is not None: domain = domain else: domain = Domain.load(DEFAULT_DOMAIN_PATH) tracker = DialogueStateTracker(dialogue.name, domain.slots) tracker.recreate_from_dialogue(dialogue) return tracker
def _create_domain(domain: Union[None, Domain, Text]) -> Domain: if isinstance(domain, str): return Domain.load(domain) elif isinstance(domain, Domain): return domain elif domain is not None: raise ValueError( "Invalid param `domain`. Expected a path to a domain " "specification or a domain instance. But got " "type '{}' with value '{}'".format(type(domain), domain))
def test_predict(http_app, app): client = RasaCoreClient(EndpointConfig(http_app)) cid = str(uuid.uuid1()) for event in test_events[:2]: client.append_event_to_tracker(cid, event) out = app.get('/domain', headers={'Accept': 'yml'}) domain = Domain.from_yaml(out.get_data()) tracker = client.tracker(cid, domain) event_dicts = [ev.as_dict() for ev in tracker.applied_events()] response = app.post('/predict', json=event_dicts) assert response.status_code == 200
def test_action(): domain = Domain.load('domain.yml') nlg = TemplatedNaturalLanguageGenerator(domain.templates) dispatcher = Dispatcher("my-sender", CollectingOutputChannel(), nlg) uid = str(uuid.uuid1()) tracker = DialogueStateTracker(uid, domain.slots) action = ActionJoke() action.run(dispatcher, tracker, domain) assert 'norris' in dispatcher.output_channel.latest_output()['text'].lower()
def _create_domain(domain): # type: (Union[None, Domain, Text]) -> Domain if isinstance(domain, string_types): return Domain.load(domain) elif isinstance(domain, Domain): return domain elif domain is not None: raise ValueError( "Invalid param `domain`. Expected a path to a domain " "specification or a domain instance. But got " "type '{}' with value '{}'".format(type(domain), domain))
def test_dispatcher_utter_buttons_from_domain_templ(default_tracker): domain_file = "examples/moodbot/domain.yml" domain = Domain.load(domain_file) bot = CollectingOutputChannel() nlg = TemplatedNaturalLanguageGenerator(domain.templates) dispatcher = Dispatcher("my-sender", bot, nlg) dispatcher.utter_template("utter_greet", default_tracker) assert len(bot.messages) == 1 assert bot.messages[0]['text'] == "Hey! How are you?" assert bot.messages[0]['buttons'] == [ {'payload': 'great', 'title': 'great'}, {'payload': 'super sad', 'title': 'super sad'} ]
def _update_model_from_server( model_server, # type: EndpointConfig agent, # type: Agent ): # type: (...) -> None """Load a zipped Rasa Core model from a URL and update the passed agent.""" if not is_url(model_server.url): raise InvalidURL(model_server.url) model_directory = tempfile.mkdtemp() new_model_fingerprint = _pull_model_and_fingerprint( model_server, model_directory, agent.fingerprint) if new_model_fingerprint: domain_path = os.path.join(os.path.abspath(model_directory), "domain.yml") domain = Domain.load(domain_path) policy_ensemble = PolicyEnsemble.load(model_directory) agent.update_model(domain, policy_ensemble, new_model_fingerprint) else: logger.debug("No new model found at " "URL {}".format(model_server.url))
def load(cls, path, # type: Text interpreter=None, # type: Optional[NaturalLanguageInterpreter] generator=None, # type: Union[EndpointConfig, NLG] tracker_store=None, # type: Optional[TrackerStore] action_endpoint=None, # type: Optional[EndpointConfig] ): # type: (...) -> Agent """Load a persisted model from the passed path.""" if not path: raise ValueError("You need to provide a valid directory where " "to load the agent from when calling " "`Agent.load`.") if os.path.isfile(path): raise ValueError("You are trying to load a MODEL from a file " "('{}'), which is not possible. \n" "The persisted path should be a directory " "containing the various model files. \n\n" "If you want to load training data instead of " "a model, use `agent.load_data(...)` " "instead.".format(path)) domain = Domain.load(os.path.join(path, "domain.yml")) ensemble = PolicyEnsemble.load(path) if path else None # ensures the domain hasn't changed between test and train domain.compare_with_specification(path) return cls(domain=domain, policies=ensemble, interpreter=interpreter, generator=generator, tracker_store=tracker_store, action_endpoint=action_endpoint)
def default_domain(self): return Domain.load(DEFAULT_DOMAIN_PATH)
def trained_policy(self, featurizer): default_domain = Domain.load(DEFAULT_DOMAIN_PATH) policy = self.create_policy(featurizer) training_trackers = train_trackers(default_domain) policy.train(training_trackers, default_domain) return policy
def test_domain_from_template(): domain_file = DEFAULT_DOMAIN_PATH domain = Domain.load(domain_file) assert len(domain.intents) == 10 assert len(domain.action_names) == 6
def test_restaurant_domain_is_valid(): # should raise no exception Domain.validate_domain_yaml(read_file( 'examples/restaurantbot/restaurant_domain.yml'))
def test_tracker_restaurant(): domain = Domain.load("data/test_domains/default_with_slots.yml") filename = 'data/test_dialogues/enter_name.json' tracker = tracker_from_dialogue_file(filename, domain) assert tracker.get_slot("name") == "holger" assert tracker.get_slot("location") is None # slot doesn't exist!
from rasa_core import training, restore from rasa_core import utils from rasa_core.actions.action import ActionListen, ACTION_LISTEN_NAME from rasa_core.channels import UserMessage from rasa_core.domain import Domain from rasa_core.events import ( UserUttered, ActionExecuted, Restarted, ActionReverted, UserUtteranceReverted) from rasa_core.tracker_store import InMemoryTrackerStore, RedisTrackerStore from rasa_core.tracker_store import ( TrackerStore) from rasa_core.trackers import DialogueStateTracker from tests.conftest import DEFAULT_STORIES_FILE from tests.utilities import tracker_from_dialogue_file, read_dialogue_file domain = Domain.load("data/test_domains/default.yml") class MockRedisTrackerStore(RedisTrackerStore): def __init__(self, domain): self.red = fakeredis.FakeStrictRedis() TrackerStore.__init__(self, domain) def stores_to_be_tested(): return [MockRedisTrackerStore(domain), InMemoryTrackerStore(domain)] def stores_to_be_tested_ids(): return ["redis-tracker",
logging.basicConfig(level=logging.DEBUG) @app.route("/nlg", methods=['POST', 'OPTIONS']) def nlg(): """Check if the server is running and responds with the version.""" nlg_call = request.json response = generate_response(nlg_call, domain) return jsonify(response) return app if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) # Running as standalone python application arg_parser = create_argument_parser() cmdline_args = arg_parser.parse_args() domain = Domain.load(cmdline_args.domain) app = create_app(domain) http_server = WSGIServer(('0.0.0.0', cmdline_args.port), app) http_server.start() logger.info("NLG endpoint is up and running. on {}" "".format(http_server.address)) http_server.serve_forever()