Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: init integration test #95

Merged
merged 46 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
15361d9
feat: init integration test
Anyc66666666 Dec 6, 2023
1245ec2
chore: add pymysql dev dependency
Anyc66666666 Dec 7, 2023
7e04382
fix: adjustment pytest
Anyc66666666 Dec 7, 2023
a66a4ca
fix: get env from secrets
Anyc66666666 Dec 7, 2023
4bd5f4b
feat: add embedding test
Anyc66666666 Dec 7, 2023
1eb2ad7
feat: support to test trace and metric
Anyc66666666 Dec 7, 2023
d34c6db
chore: ignore mypy in tests
Anyc66666666 Dec 7, 2023
19cdb68
fix: solve lint
Anyc66666666 Dec 7, 2023
6af0ffe
feat: disable parallel
Anyc66666666 Dec 11, 2023
648b030
chore: solve packages import
Anyc66666666 Dec 11, 2023
1f6b6bb
feat: clarify logic and add fixtures to truncate database
Anyc66666666 Dec 11, 2023
3152778
fix: solve lint
Anyc66666666 Dec 11, 2023
0cef2b0
chore: run ci in order
Anyc66666666 Dec 11, 2023
a12b11d
fix: literate py version with str
Anyc66666666 Dec 11, 2023
74b4f60
chore: optimize table name and doc string
Anyc66666666 Dec 11, 2023
a779568
chore: ignore numbers table
Anyc66666666 Dec 11, 2023
63928a6
chore: use shared steps
Anyc66666666 Dec 11, 2023
d7a7d0a
fix: optimize ci
Anyc66666666 Dec 11, 2023
8acf8a6
fix: optimize ci
Anyc66666666 Dec 11, 2023
955540d
fix: optimize composite actions
Anyc66666666 Dec 11, 2023
98d623a
fix: optimize composite actions
Anyc66666666 Dec 11, 2023
1a9e3c6
fix: fix composite actions
Anyc66666666 Dec 11, 2023
bf07122
fix: repair composite actions
Anyc66666666 Dec 11, 2023
5118975
fix: add composite using
Anyc66666666 Dec 11, 2023
5365267
fix: fix lint
Anyc66666666 Dec 11, 2023
bf47ccb
ci: refactor ci
Anyc66666666 Dec 13, 2023
6efd980
chore: delete action
Anyc66666666 Dec 13, 2023
d840f64
feat: add stream,async and raw_response tests for chat_completion
Anyc66666666 Dec 13, 2023
a22ae40
chore: remove metrics test
Anyc66666666 Dec 13, 2023
322c871
chore: add client init
Anyc66666666 Dec 13, 2023
d280db2
chore: add pytest-asyncio to test async
Anyc66666666 Dec 13, 2023
1f83239
fix: solve lint
Anyc66666666 Dec 13, 2023
81058a7
fix: correct naming
Anyc66666666 Dec 13, 2023
f67a1a7
feat: use global collector
Anyc66666666 Dec 14, 2023
33d215b
feat: add force flush func
Anyc66666666 Dec 14, 2023
a808233
chore: optimize flush
Anyc66666666 Dec 14, 2023
3b1f9b5
Revert "chore: optimize flush"
Anyc66666666 Dec 14, 2023
6b1206d
chore: optimize flush
Anyc66666666 Dec 14, 2023
570ee77
chore: adjust flush response time
Anyc66666666 Dec 14, 2023
085edf7
chore: fix lint
Anyc66666666 Dec 14, 2023
c267518
chore: extend sleep time
Anyc66666666 Dec 14, 2023
268d2b2
chore: remove extra tests for now
Anyc66666666 Dec 14, 2023
06cc3f5
chore: remove extra sql
Anyc66666666 Dec 14, 2023
84685e8
feat: add pymysql types
Anyc66666666 Dec 14, 2023
14f1f43
feat: add pymysql types
Anyc66666666 Dec 14, 2023
58ce0b6
feat: install pymysql types in ci
Anyc66666666 Dec 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
ci:
runs-on: ubuntu-latest
strategy:
max-parallel: 1
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]

Expand All @@ -41,10 +42,17 @@ jobs:

- name: Install Dev dependencies
run: |
pip install langchain openai pytest ruff mypy
pip install langchain openai pytest ruff mypy pymysql pytest-asyncio types-PyMySQL

# TODO(yuanbohan): code coverage with pytest-cov
- name: Test with pytest
env:
GREPTIMEAI_HOST: ${{ secrets.GREPTIMEAI_HOST }}
GREPTIMEAI_USERNAME: ${{ secrets.GREPTIMEAI_USERNAME }}
GREPTIMEAI_PASSWORD: ${{ secrets.GREPTIMEAI_PASSWORD }}
GREPTIMEAI_DATABASE: ${{ secrets.GREPTIMEAI_DATABASE }}
GREPTIMEAI_TOKEN: ${{ secrets.GREPTIMEAI_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
pytest tests/ -v

yuanbohan marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -56,3 +64,7 @@ jobs:
- name: Static Type Checking with mypy
run: |
mypy $(git ls-files '*.py') --check-untyped-defs

concurrency:
group: ${{ github.repository }}
cancel-in-progress: true
yuanbohan marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ dev-dependencies = [
"langchain>=0.0.27",
"openai>=1.3.5",
"mypy>=1.7.1",
"pymysql>=1.1.0",
"pytest-asyncio>=0.23.2",
]

[tool.rye.scripts]
Expand Down
7 changes: 4 additions & 3 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ distro==1.8.0
exceptiongroup==1.1.3
frozenlist==1.4.0
googleapis-common-protos==1.61.0
greenlet==3.0.1
h11==0.14.0
httpcore==1.0.2
httpx==0.25.1
Expand All @@ -47,16 +46,18 @@ opentelemetry-sdk==1.20.0
opentelemetry-semantic-conventions==0.41b0
packaging==23.2
pluggy==1.3.0
protobuf==4.24.4
protobuf==4.25.1
pydantic==2.4.2
pydantic-core==2.10.1
pymysql==1.1.0
pytest==7.4.3
pytest-asyncio==0.23.2
pyyaml==6.0.1
regex==2023.10.3
requests==2.31.0
ruff==0.1.3
sniffio==1.3.0
sqlalchemy==2.0.22
sqlalchemy==2.0.23
tenacity==8.2.3
tiktoken==0.5.1
tomli==2.0.1
Expand Down
25 changes: 14 additions & 11 deletions src/greptimeai/openai_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
)
from greptimeai.patcher.openai_patcher.retry import _RetryPatcher

_collector: Collector = None # type: ignore


def setup(
host: str = "",
Expand All @@ -37,20 +39,21 @@ def setup(
token: if None or empty string, GREPTIMEAI_TOKEN environment variable will be used.
client: if None, then openai module-level client will be patched.
"""
collector = Collector(
global _collector
_collector = Collector(
service_name="openai", host=host, database=database, token=token
)
patchers: List[Patcher] = [
_AudioPatcher(collector=collector, client=client),
_ChatCompletionPatcher(collector=collector, client=client),
_CompletionPatcher(collector=collector, client=client),
_EmbeddingPatcher(collector=collector, client=client),
_FilePatcher(collector=collector, client=client),
_FineTuningPatcher(collector=collector, client=client),
_ImagePatcher(collector=collector, client=client),
_ModelPatcher(collector=collector, client=client),
_ModerationPatcher(collector=collector, client=client),
_RetryPatcher(collector=collector, client=client),
_AudioPatcher(collector=_collector, client=client),
_ChatCompletionPatcher(collector=_collector, client=client),
_CompletionPatcher(collector=_collector, client=client),
_EmbeddingPatcher(collector=_collector, client=client),
_FilePatcher(collector=_collector, client=client),
_FineTuningPatcher(collector=_collector, client=client),
_ImagePatcher(collector=_collector, client=client),
_ModelPatcher(collector=_collector, client=client),
_ModerationPatcher(collector=_collector, client=client),
_RetryPatcher(collector=_collector, client=client),
]

for patcher in patchers:
Expand Down
Empty file added tests/__init__.py
Empty file.
Empty file added tests/collection/__init__.py
Empty file.
Empty file added tests/integrations/__init__.py
Empty file.
Empty file.
58 changes: 58 additions & 0 deletions tests/integrations/database/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import logging
import os
from typing import Union, List

import pymysql

from .model import Tables

db = pymysql.connect(
host=os.getenv("GREPTIMEAI_HOST"),
user=os.getenv("GREPTIMEAI_USERNAME"),
passwd=os.getenv("GREPTIMEAI_PASSWORD"),
port=4002,
db=os.getenv("GREPTIMEAI_DATABASE"),
)
cursor = db.cursor()

trace_sql = "SELECT model,prompt_tokens,completion_tokens FROM %s WHERE user_id = '%s'"
truncate_sql = "TRUNCATE %s"


def get_trace_data(user_id: str) -> List[Union[str, int]]:
"""
get trace data for llm trace by user_id
:param is_stream:
:param user_id:
:return: model, prompt_tokens, completion_tokens
"""

cursor.execute(trace_sql % (Tables.llm_trace, user_id))
trace = cursor.fetchone()
if trace is None:
raise Exception("trace data is None")
return list(trace)


def truncate_tables():
"""
truncate all tables
:return:
"""
tables = [
"llm_completion_tokens",
"llm_completion_tokens_cost",
"llm_errors",
"llm_prompt_tokens",
"llm_prompt_tokens_cost",
"llm_request_duration_ms_bucket",
"llm_request_duration_ms_count",
"llm_request_duration_ms_sum",
"llm_traces_preview_v01",
]
try:
cursor.executemany(truncate_sql, tables)
db.commit()
except Exception as e:
logging.error(e)
db.rollback()
88 changes: 88 additions & 0 deletions tests/integrations/database/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
class LlmTrace(object):
table_name = "llm_traces_preview_v01"

trace_id: str
span_id: str
parent_span_id: str
resource_attributes: str
scope_name: str
scope_version: str
scope_attributes: str
trace_state: str
span_name: str
span_kind: str
span_status_code: str
span_status_message: str
span_attributes: str
span_events: str
span_links: str
start: float
end: float
user_id: str
model: str
prompt_tokens: int
prompt_cost: float
completion_tokens: int
completion_cost: float
greptime_value: str
greptime_timestamp: float


class LlmPromptToken(object):
table_name = "llm_prompt_tokens"

telemetry_sdk_language: str
telemetry_sdk_name: str
telemetry_sdk_version: str
service_name: str
span_name: str
model: str
greptime_value: str
greptime_timestamp: float


class LlmPromptTokenCost(object):
table_name = "llm_prompt_tokens_cost"

telemetry_sdk_language: str
telemetry_sdk_name: str
telemetry_sdk_version: str
service_name: str
span_name: str
model: str
greptime_value: str
greptime_timestamp: float


class LlmCompletionToken(object):
table_name = "llm_completion_tokens"

telemetry_sdk_language: str
telemetry_sdk_name: str
telemetry_sdk_version: str
service_name: str
span_name: str
model: str
greptime_value: str
greptime_timestamp: float


class LlmCompletionTokenCost(object):
table_name = "llm_completion_tokens_cost"

telemetry_sdk_language: str
telemetry_sdk_name: str
telemetry_sdk_version: str
service_name: str
span_name: str
model: str
greptime_value: str
greptime_timestamp: float


class Tables(object):
llm_trace = "llm_traces_preview_v01"
llm_prompt_tokens = "llm_prompt_tokens"
llm_prompt_tokens_cost = "llm_prompt_tokens_cost"
llm_completion_tokens = "llm_completion_tokens"
llm_completion_tokens_cost = "llm_completion_tokens_cost"
10 changes: 10 additions & 0 deletions tests/integrations/openai_tracker/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from openai import AsyncOpenAI
from openai import OpenAI

from greptimeai import openai_patcher # type: ignore
yuanbohan marked this conversation as resolved.
Show resolved Hide resolved

async_client = AsyncOpenAI()
openai_patcher.setup(client=async_client)

client = OpenAI()
openai_patcher.setup(client=client)
43 changes: 43 additions & 0 deletions tests/integrations/openai_tracker/test_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import time
import uuid

import pytest

from ..database.db import (
get_trace_data,
truncate_tables,
)
from ..openai_tracker import client
from greptimeai.openai_patcher import _collector # type: ignore


@pytest.fixture
def _truncate_tables():
truncate_tables()
yield


def test_chat_completion(_truncate_tables):
user_id = str(uuid.uuid4())
resp = client.chat.completions.create(
messages=[
{
"role": "user",
"content": "1+1=",
}
],
model="gpt-3.5-turbo",
user=user_id,
seed=1,
)
assert resp.choices[0].message.content == "2"

_collector._collector._force_flush()
time.sleep(6)
trace = get_trace_data(user_id)

assert resp.model == trace[0]

if resp.usage:
assert resp.usage.prompt_tokens == trace[1]
assert resp.usage.completion_tokens == trace[2]
Empty file added tests/langchain/__init__.py
Empty file.
Empty file added tests/utils/__init__.py
Empty file.
Loading