Skip to content

Commit

Permalink
Enhance logging and error handling in Yandex GPT integration
Browse files Browse the repository at this point in the history
- Added GITHUB_SHA to logger context for better traceability.
- Improved logging structure in the completion endpoint with contextualized logging.
- Implemented message obfuscation for sensitive information in logs.
- Updated Yandex API key handling and error responses for robustness.
- Minor adjustments in test configuration for local development.

These changes improve the reliability and security of the Yandex integration, ensuring sensitive data is protected while enhancing the overall logging capabilities.
  • Loading branch information
all-mute committed Dec 12, 2024
1 parent 011b09b commit 84eb289
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 47 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/delpoy-slsc-prod-stable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Deploy Serverless Container to Yandex Cloud (prod-stable)

on:
push:
branches: [ "stable" ]

jobs:

delpoy-stable:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Login to Yandex Cloud Container Registry
id: login-cr
uses: yc-actions/yc-cr-login@v1
with:
yc-sa-json-credentials: ${{ secrets.YC_SA_JSON_CREDENTIALS }}

- name: Build, tag, and push image to Yandex Cloud Container Registry
env:
CR_REGISTRY: crpel88gtvoc0esr11nd
CR_REPOSITORY: my-repo-prod-stable
IMAGE_TAG: ${{ github.sha }}

run: |
ls -a
docker build -t cr.yandex/$CR_REGISTRY/$CR_REPOSITORY:$IMAGE_TAG -f YC-Dockerfile .
docker push cr.yandex/$CR_REGISTRY/$CR_REPOSITORY:$IMAGE_TAG
- name: Deploy Serverless Container
id: deploy-sls-container
uses: yc-actions/[email protected]

with:
yc-sa-json-credentials: ${{ secrets.YC_SA_JSON_CREDENTIALS }}
container-name: adapter-0-prod-stable
folder-id: b1gkvpmciuf1at2nkvcb
revision-service-account-id: ajekbtndj8s14af74itc
revision-cores: 1
revision-memory: 128Mb
revision-core-fraction: 100
revision-concurrency: 2
revision-provisioned: 1
revision-image-url: cr.yandex/crpel88gtvoc0esr11nd/my-repo-prod-stable:${{ github.sha }}
revision-execution-timeout: 120
public: true

revision-log-options-log-group-id: e23p2mbdc5gis4an2ess
revision-log-options-min-level: level_unspecified

revision-env: |
GITHUB_SHA=${{ github.sha }}
GITHUB_REF=${{ github.ref }}
LOG_TYPE=yc
LOG_LEVEL=INFO
100 changes: 53 additions & 47 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

load_dotenv()

GITHUB_SHA = os.getenv("GITHUB_SHA", "unknown_version")

logger.configure(extra={"GITHUB_SHA": GITHUB_SHA})
logger.info("App module initiated.")

# Загрузка конфига
Expand All @@ -32,57 +35,60 @@

@app.post("/v1/chat/completions")
async def completion(request: Request):
logger.info("Обработка запроса на генерацию ответа.", extra={"my-key": "my-value"})
logger.debug(f"Запрос: {request.method} {request.url}")
logger.debug(f"Заголовки: {request.headers}")
logger.debug(f"Тело запроса: {await request.json()}")
logger.debug(f"IP-адрес отправителя: {request.client.host}")
try:
logger.debug("Получение OPENAI_API_KEY из заголовка запроса.")
openai_api_key = request.headers.get("Authorization", "").split("Bearer ")[-1].strip()
with logger.contextualize(timestamp=time.time()):
logger.info("Обработка запроса на генерацию ответа.", extra={"my-key": "my-value"})
logger.debug(f"Запрос: {request.method} {request.url}")
logger.debug(f"Заголовки: {request.headers}")
logger.debug(f"Тело запроса: {await request.json()}")
logger.debug(f"IP-адрес отправителя: {request.client.host}")

logger.debug(f"Извлеченный OPENAI_API_KEY: {openai_api_key}")

# Определение Yandex API ключа и ID папки
if openai_api_key in autoauth_keys:
yandex_api_key, folder_id = YANDEX_API_KEY, FOLDER_ID
logger.debug("Использование Yandex API ключа из переменных окружения.")
else:
folder_id, yandex_api_key = openai_api_key.split("@")
logger.debug(f"Использование Yandex API ключа: {yandex_api_key} и ID папки: {folder_id}.")

# Получение данных из запроса
body = await request.json()
model = body.get("model")
max_tokens = body.get("max_tokens", 2048)
temperature = body.get("temperature", 0.3)
messages = body.get("messages", [])
tools = body.get("tools", None)
stream = body.get("stream", False)

logger.debug(f"Полученные данные: model={model}, max_tokens={max_tokens}, temperature={temperature}, messages={messages}, tools={tools}, stream={stream}")
logger.info(f"Используемая модель: {model}")

# Генерация ответа от Yandex GPT
if stream:
try:
logger.debug("Получение OPENAI_API_KEY из заголовка запроса.")
openai_api_key = request.headers.get("Authorization", "").split("Bearer ")[-1].strip()

return StreamingResponse(generate_yandexgpt_stream_response(messages, tools, model, temperature, max_tokens, yandex_api_key, folder_id), media_type="text/event-stream")
else:
yandex_response, yandex_error = await generate_yandexgpt_response(messages, tools, model, temperature, max_tokens, yandex_api_key, folder_id)

if yandex_error:
logger.error(f"Ошибка при генерации ответа от Yandex GPT: {yandex_error}")
return yandex_error
logger.debug(f"Извлеченный OPENAI_API_KEY: {openai_api_key}")

openai_format_response = _adapt_message_for_openai(yandex_response, model)
return openai_format_response

except HTTPException as e:
logger.error(f"HTTP ошибка: {str(e)}")
return {"error": str(e)} # Возврат ошибки в формате JSON
except Exception as e:
logger.error(f"Неожиданная ошибка: {str(e)}")
return {"error": "An unexpected error occurred."} # Обработка неожиданных ошибок
# Определение Yandex API ключа и ID папки
if openai_api_key in autoauth_keys:
yandex_api_key, folder_id = YANDEX_API_KEY, FOLDER_ID
logger.debug("Использование Yandex API ключа из переменных окружения.")
else:
folder_id, yandex_api_key = openai_api_key.split("@")
logger.debug(f"Использование Yandex Api-Key: {yandex_api_key} и ID папки: {folder_id}.")

# Получение данных из запроса
body = await request.json()
model = body.get("model")
max_tokens = body.get("max_tokens", 2048)
temperature = body.get("temperature", 0.3)
messages = body.get("messages", [])
tools = body.get("tools", None)
stream = body.get("stream", False)

logger.debug(f"Полученные данные: model={model}, max_tokens={max_tokens}, temperature={temperature}, messages={messages}, tools={tools}, stream={stream}")
logger.info(f"Используемая модель: {model}")

# Генерация ответа от Yandex GPT
if stream:

return StreamingResponse(generate_yandexgpt_stream_response(messages, tools, model, temperature, max_tokens, yandex_api_key, folder_id), media_type="text/event-stream")
else:
yandex_response, yandex_error = await generate_yandexgpt_response(messages, tools, model, temperature, max_tokens, yandex_api_key, folder_id)

if yandex_error:
logger.error(f"Ошибка при генерации ответа от Yandex GPT: {yandex_error}")
return yandex_error

openai_format_response = _adapt_message_for_openai(yandex_response, model)
return openai_format_response

except HTTPException as e:
logger.error(f"HTTP ошибка: {str(e)}")
return {"error": str(e)} # Возврат ошибки в формате JSON
except Exception as e:
logger.error(f"Неожиданная ошибка: {str(e)}")
return {"error": "An unexpected error occurred."} # Обработка неожиданных ошибок

@app.post("/v1/embeddings")
async def embeddings(request: Request):
Expand Down
14 changes: 14 additions & 0 deletions app/yc_log_handler.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import logging
from pythonjsonlogger import jsonlogger
import dotenv
import os
import re

dotenv.load_dotenv()

YANDEX_API_KEY = os.getenv("YANDEX_API_KEY")

def obfuscate_message(message: str):
"""Obfuscate sensitive information."""
result = re.sub(r'(Api-Key|Bearer|OAuth|OPENAI_API_KEY:|Ключ:|ключ:) [A-Za-z0-9_\-@]+', "***API_KEY_OBFUSCATED***", message)
return result

class YcLoggingFormatter(jsonlogger.JsonFormatter):
def add_fields(self, log_record, record, message_dict):
record.message = obfuscate_message(record.getMessage())

super(YcLoggingFormatter, self).add_fields(log_record, record, message_dict)
log_record['logger'] = record.name
log_record['level'] = str.replace(str.replace(record.levelname, "WARNING", "WARN"), "CRITICAL", "FATAL")
Expand Down
1 change: 1 addition & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
API_KEY = os.getenv("YANDEX_API_KEY", "")
#PROXY_URL = "https://d5det46m4e43042pnnfj.apigw.yandexcloud.net"
PROXY_URL = "http://localhost:9041"
#PROXY_URL = "https://bbafv6hdkrihhcvh9u78.containers.yandexcloud.net"

system_prompt = "Answer with only one word to my question"
user_prompt = "What is the meaning of life?"
Expand Down

0 comments on commit 84eb289

Please sign in to comment.