chore: import upstream snapshot with attribution
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
Publish Promptflow Doc / Build (push) Has been cancelled
Publish Promptflow Doc / Deploy (push) Has been cancelled
Flake8 Lint / flake8 (push) Has been cancelled
Spell check CI / Spell_Check (push) Has been cancelled
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 839 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 357 KiB |
|
After Width: | Height: | Size: 553 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 446 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,207 @@
|
||||
from datetime import datetime
|
||||
import time
|
||||
import requests
|
||||
import sys
|
||||
import json
|
||||
from azure.identity import AzureCliCredential
|
||||
import logging
|
||||
from azure.ai.ml import MLClient
|
||||
from sseclient import SSEClient
|
||||
|
||||
|
||||
class ColoredFormatter(logging.Formatter):
|
||||
# Color code dictionary
|
||||
color_codes = {
|
||||
'debug': '\033[0;32m', # Green
|
||||
'info': '\033[0;36m', # Cyan
|
||||
'warning': '\033[0;33m', # Yellow
|
||||
'error': '\033[0;31m', # Red
|
||||
'critical': '\033[0;35m', # Magenta
|
||||
}
|
||||
|
||||
def format(self, record):
|
||||
# Get the original message
|
||||
message = super().format(record)
|
||||
|
||||
# Add color codes
|
||||
message = f"{self.color_codes.get(record.levelname.lower(), '')}{message}\033[0m"
|
||||
|
||||
return message
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(ColoredFormatter())
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.addHandler(handler)
|
||||
|
||||
|
||||
def apply_delta(base: dict, delta: dict):
|
||||
for k, v in delta.items():
|
||||
if k in base:
|
||||
base[k] += v
|
||||
else:
|
||||
base[k] = v
|
||||
|
||||
|
||||
def score(url, api_key, body, stream=True, on_event=None):
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": ("Bearer " + api_key),
|
||||
# The azureml-model-deployment header will force the request to go to a specific deployment.
|
||||
# Remove this header to have the request observe the endpoint traffic rules
|
||||
"azureml-model-deployment": "blue",
|
||||
"Accept": "text/event-stream, application/json" if stream else "application/json"
|
||||
}
|
||||
|
||||
logger.info("Sending HTTP request...")
|
||||
logger.debug("POST %s", url)
|
||||
for name, value in headers.items():
|
||||
if name == "Authorization":
|
||||
value = "[REDACTED]"
|
||||
logger.debug(f">>> {name}: {value}")
|
||||
logger.debug(json.dumps(body, indent=4, ensure_ascii=False))
|
||||
logger.debug("")
|
||||
|
||||
time1 = datetime.now()
|
||||
response = None
|
||||
try:
|
||||
response = requests.post(url, json=body, headers=headers, stream=stream)
|
||||
response.raise_for_status()
|
||||
finally:
|
||||
time2 = datetime.now()
|
||||
if response is not None:
|
||||
logger.info(
|
||||
"Got response: %d %s (elapsed %s)",
|
||||
response.status_code,
|
||||
response.reason,
|
||||
time2 - time1,
|
||||
)
|
||||
for name, value in response.headers.items():
|
||||
logger.debug(f"<<< {name}: {value}")
|
||||
|
||||
time1 = datetime.now()
|
||||
try:
|
||||
content_type = response.headers.get('Content-Type')
|
||||
if "text/event-stream" in content_type:
|
||||
output = {}
|
||||
client = SSEClient(response)
|
||||
for event in client.events():
|
||||
if on_event:
|
||||
on_event(event)
|
||||
|
||||
dct = json.loads(event.data)
|
||||
apply_delta(output, dct)
|
||||
return output, True
|
||||
else:
|
||||
return response.json(), False
|
||||
finally:
|
||||
time2 = datetime.now()
|
||||
logger.info("\nResponse reading elapsed: %s", time2 - time1)
|
||||
|
||||
|
||||
class ChatApp:
|
||||
def __init__(self, ml_client, endpoint_name, chat_input_name, chat_output_name, stream=True, debug=False):
|
||||
self._chat_input_name = chat_input_name
|
||||
self._chat_output_name = chat_output_name
|
||||
|
||||
self._chat_history = []
|
||||
self._stream = stream
|
||||
if debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
logger.info("Getting endpoint info...")
|
||||
endpoint = ml_client.online_endpoints.get(endpoint_name)
|
||||
keys = ml_client.online_endpoints.get_keys(endpoint_name)
|
||||
self._endpoint_url = endpoint.scoring_uri
|
||||
self._endpoint_key = keys.primary_key if endpoint.auth_mode == "key" else keys.access_token
|
||||
|
||||
logger.info(f"Done.")
|
||||
logger.debug(f"Target endpoint: {endpoint.id}")
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self._endpoint_url
|
||||
|
||||
@property
|
||||
def api_key(self):
|
||||
return self._endpoint_key
|
||||
|
||||
def get_payload(self, chat_input, chat_history=[]):
|
||||
return {
|
||||
self._chat_input_name: chat_input,
|
||||
"chat_history": chat_history,
|
||||
}
|
||||
|
||||
def chat_once(self, chat_input):
|
||||
def on_event(event):
|
||||
dct = json.loads(event.data)
|
||||
answer_delta = dct.get(self._chat_output_name)
|
||||
if answer_delta:
|
||||
print(answer_delta, end='')
|
||||
# We need to flush the output
|
||||
# otherwise the text does not appear on the console
|
||||
# unless a new line comes.
|
||||
sys.stdout.flush()
|
||||
# Sleep for 20ms for better animation effects
|
||||
time.sleep(0.02)
|
||||
|
||||
try:
|
||||
payload = self.get_payload(chat_input=chat_input, chat_history=self._chat_history)
|
||||
output, stream = score(self.url, self.api_key, payload, stream=self._stream, on_event=on_event)
|
||||
# We don't use self._stream here since the result may not always be the same as self._stream specified.
|
||||
if stream:
|
||||
# Print a new line at the end of the content to make sure
|
||||
# the next logger line will always starts from a new line.
|
||||
pass
|
||||
# print("\n")
|
||||
else:
|
||||
print(output.get(self._chat_output_name, "<empty>"))
|
||||
|
||||
self._chat_history.append({
|
||||
"inputs": {
|
||||
self._chat_input_name: chat_input,
|
||||
},
|
||||
"outputs": output,
|
||||
})
|
||||
logger.info("Length of chat history: %s", len(self._chat_history))
|
||||
except requests.HTTPError as e:
|
||||
logger.error(e.response.text)
|
||||
|
||||
def chat(self):
|
||||
while True:
|
||||
try:
|
||||
question = input("Chat with Wikipedia:> ")
|
||||
if question in ("exit", "bye"):
|
||||
print("Bye.")
|
||||
break
|
||||
self.chat_once(question)
|
||||
except KeyboardInterrupt:
|
||||
# When pressed Ctrl_C, exit
|
||||
print("\nBye.")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.exception("An error occurred: %s", e)
|
||||
# Do not raise the errors out so that we can continue the chat
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ml_client = MLClient(
|
||||
credential=AzureCliCredential(),
|
||||
# Replace with your subscription ID, resource group name, and workspace name
|
||||
subscription_id="<your_sub_id>",
|
||||
resource_group_name="<your_resource_group_name>",
|
||||
workspace_name="<your_workspace_name>",
|
||||
)
|
||||
|
||||
chat_app = ChatApp(
|
||||
ml_client=ml_client,
|
||||
# TODO: Replace with your online endpoint name
|
||||
endpoint_name="chat-with-wikipedia-stream",
|
||||
chat_input_name="question",
|
||||
chat_output_name="answer",
|
||||
stream=False,
|
||||
debug=True,
|
||||
)
|
||||
|
||||
chat_app.chat()
|
||||
@@ -0,0 +1,3 @@
|
||||
azure-identity
|
||||
azure-ai-ml
|
||||
sseclient-py
|
||||
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 794 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 154 KiB |