Skip to content

Commit

Permalink
fix: fix handling of datetime with less than 3 fractional digits for …
Browse files Browse the repository at this point in the history
…seconds (#14) (#16)

* fix: fix handling of datetime with less than 3 fractional digits for seconds (#14)

* Fix pyright error: parser is not a known member of module dateutil

* Add tests for parse_time
  • Loading branch information
Allob authored Nov 28, 2023
1 parent 7d181ef commit b610069
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 25 deletions.
8 changes: 3 additions & 5 deletions aidial_analytics_realtime/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import logging
import re
from datetime import datetime, timezone
from datetime import datetime

import uvicorn
from fastapi import Depends, FastAPI, Request
Expand All @@ -12,6 +12,7 @@
create_influx_writer,
)
from aidial_analytics_realtime.rates import RatesCalculator
from aidial_analytics_realtime.time import parse_time
from aidial_analytics_realtime.topic_model import TopicModel
from aidial_analytics_realtime.universal_api_utils import merge

Expand Down Expand Up @@ -173,10 +174,7 @@ async def on_log_message(
response["upstream_uri"] if "upstream_uri" in response else ""
)

timestamp = datetime.fromisoformat(request["time"])
if timestamp.tzinfo is None:
# The logs may come without the timezone information. We want it to be interpreted as UTC, not local time.
timestamp = timestamp.replace(tzinfo=timezone.utc)
timestamp = parse_time(request["time"])

match = re.search(RATE_PATTERN, uri)
if match:
Expand Down
11 changes: 11 additions & 0 deletions aidial_analytics_realtime/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from datetime import datetime, timezone

import dateutil.parser as dateutil_parser


def parse_time(time_string: str) -> datetime:
timestamp = dateutil_parser.isoparse(time_string)
if timestamp.tzinfo is None:
# The logs may come without the timezone information. We want it to be interpreted as UTC, not local time.
timestamp = timestamp.replace(tzinfo=timezone.utc)
return timestamp
4 changes: 2 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
SRC = "."


@nox.session
@nox.session(python=["3.10"])
def lint(session):
"""Runs linters and fixers"""
session.run("poetry", "install", external=True)
Expand All @@ -17,7 +17,7 @@ def lint(session):
session.run("pyright", SRC)


@nox.session
@nox.session(python=["3.10"])
def format(session):
"""Runs linters and fixers"""
session.run("poetry", "install", external=True)
Expand Down
17 changes: 1 addition & 16 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ torch = {version = "^2.0.1+cpu", source = "pytorch"}
bertopic = "^0.15.0"
llvmlite = "^0.40.1"
python-dotenv = "^1.0.0"
python-dateutil = "^2.8.2"

[[tool.poetry.source]]
name = "pytorch"
Expand Down
38 changes: 36 additions & 2 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,44 @@ def test_data_request():
},
}
)
}
},
{
"message": json.dumps(
{
"apiType": "DialOpenAI",
"chat": {"id": "chat-2"},
"project": {"id": "PROJECT-KEY-2"},
"user": {"id": "", "title": ""},
"request": {
"protocol": "HTTP/1.1",
"method": "POST",
"uri": "/openai/deployments/gpt-4/chat/completions",
"time": "2023-11-24T03:33:40.39",
"body": json.dumps(
{
"messages": [
{"role": "system", "content": ""},
{"role": "user", "content": "Hi!"},
],
"model": "gpt-4",
"max_tokens": 2000,
"stream": True,
"n": 1,
"temperature": 0.0,
}
),
},
"response": {
"status": "200",
"body": 'data: {"id":"chatcmpl-2","object":"chat.completion.chunk","created":1700828102,"model":"gpt-4","choices":[{"index":0,"delta":{"role":"assistant","content":"Hi"},"finish_reason":null}]}\n\ndata: {"id":"chatcmpl-2","object":"chat.completion.chunk","created":1700828102,"model":"gpt-4","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"completion_tokens":189,"prompt_tokens":22,"total_tokens":211}}\n\ndata: [DONE]\n',
},
}
)
},
],
)
assert response.status_code == 200
assert write_api_mock.points == [
'analytics,deployment=gpt-4,language=undefined,model=gpt-4,project_id=PROJECT-KEY,response_id=chatcmpl-1,title=undefined,topic=TestTopic,upstream=undefined chat_id="chat-1",completion_tokens=189i,number_request_messages=2i,price=0,prompt_tokens=22i,user_hash="undefined" 1692214959997000000'
'analytics,deployment=gpt-4,language=undefined,model=gpt-4,project_id=PROJECT-KEY,response_id=chatcmpl-1,title=undefined,topic=TestTopic,upstream=undefined chat_id="chat-1",completion_tokens=189i,number_request_messages=2i,price=0,prompt_tokens=22i,user_hash="undefined" 1692214959997000000',
'analytics,deployment=gpt-4,language=undefined,model=gpt-4,project_id=PROJECT-KEY-2,response_id=chatcmpl-2,title=undefined,topic=TestTopic,upstream=undefined chat_id="chat-2",completion_tokens=189i,number_request_messages=2i,price=0,prompt_tokens=22i,user_hash="undefined" 1700796820390000000',
]
64 changes: 64 additions & 0 deletions tests/test_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from datetime import datetime, timedelta, timezone

import pytest

from aidial_analytics_realtime.time import parse_time


@pytest.mark.parametrize(
"time_string, expected",
[
(
"2011-12-03T10:15:30",
datetime(2011, 12, 3, 10, 15, 30, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30+01:00",
datetime(
2011, 12, 3, 10, 15, 30, tzinfo=timezone(timedelta(hours=1))
),
),
(
"2011-12-03T10:15:30.1",
datetime(2011, 12, 3, 10, 15, 30, 100000, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30.12",
datetime(2011, 12, 3, 10, 15, 30, 120000, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30.123",
datetime(2011, 12, 3, 10, 15, 30, 123000, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30.1234",
datetime(2011, 12, 3, 10, 15, 30, 123400, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30.12345",
datetime(2011, 12, 3, 10, 15, 30, 123450, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30.123456",
datetime(2011, 12, 3, 10, 15, 30, 123456, tzinfo=timezone.utc),
),
(
"2011-12-03T10:15:30.1234567",
datetime(2011, 12, 3, 10, 15, 30, 123456, tzinfo=timezone.utc),
), # Python's datetime supports up to microsecond precision
],
)
def test_parse_time(time_string: str, expected: datetime):
assert parse_time(time_string) == expected


@pytest.mark.parametrize(
"time_string",
[
"2011-12-03T10:15:30.", # No fractional part
"2011-12-03T10:15:30+01:00[Europe/Paris]", # Named timezones are not supported
],
)
def test_parse_time_should_fail(time_string: str):
with pytest.raises(ValueError):
parse_time(time_string)

0 comments on commit b610069

Please sign in to comment.