Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python 3.11
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: "3.12"

- name: Cache pip dependencies
uses: actions/cache@v4
Expand Down
2 changes: 2 additions & 0 deletions backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class Settings(BaseSettings):
chunk_overlap: int = 50
max_retrieval_results: int = 5
similarity_threshold: float = 0.3
openai_timeout: float = 30.0
openai_connect_timeout: float = 10.0

model_config = {"env_file": ".env"}

Expand Down
9 changes: 8 additions & 1 deletion backend/services/embedding_service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

import httpx
from openai import OpenAI

from config import settings
Expand All @@ -12,7 +13,13 @@
def _get_client() -> OpenAI:
global _client
if _client is None:
_client = OpenAI(api_key=settings.openai_api_key)
_client = OpenAI(
api_key=settings.openai_api_key,
timeout=httpx.Timeout(
settings.openai_timeout,
connect=settings.openai_connect_timeout,
),
)
return _client


Expand Down
9 changes: 8 additions & 1 deletion backend/services/rag_service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

import httpx
from openai import OpenAI

from config import settings
Expand All @@ -15,7 +16,13 @@
def _get_client() -> OpenAI:
global _client
if _client is None:
_client = OpenAI(api_key=settings.openai_api_key)
_client = OpenAI(
api_key=settings.openai_api_key,
timeout=httpx.Timeout(
settings.openai_timeout,
connect=settings.openai_connect_timeout,
),
)
return _client


Expand Down
113 changes: 113 additions & 0 deletions backend/tests/test_timeout_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""OpenAI APIタイムアウト設定のユニットテスト"""

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from unittest.mock import patch, MagicMock # noqa: E402

import httpx # noqa: E402


class TestRagServiceTimeout:
"""rag_service の OpenAI クライアントタイムアウト設定テスト"""

def setup_method(self):
"""各テストの前にクライアントキャッシュをリセット"""
import services.rag_service as mod
mod._client = None

@patch("services.rag_service.OpenAI")
def test_rag_client_has_timeout(self, mock_openai_cls):
"""rag_service の _get_client が httpx.Timeout を設定して OpenAI を初期化する"""
mock_openai_cls.return_value = MagicMock()

from services.rag_service import _get_client
_get_client()

mock_openai_cls.assert_called_once()
call_kwargs = mock_openai_cls.call_args[1]
assert "timeout" in call_kwargs
timeout = call_kwargs["timeout"]
assert isinstance(timeout, httpx.Timeout)
assert timeout.read == 30.0
assert timeout.connect == 10.0

@patch("services.rag_service.OpenAI")
def test_rag_client_uses_config_values(self, mock_openai_cls):
"""rag_service が config の設定値を使用してタイムアウトを設定する"""
mock_openai_cls.return_value = MagicMock()

with patch("services.rag_service.settings") as mock_settings:
mock_settings.openai_api_key = "test-key"
mock_settings.openai_timeout = 60.0
mock_settings.openai_connect_timeout = 20.0

import services.rag_service as mod
mod._client = None

from services.rag_service import _get_client
_get_client()

call_kwargs = mock_openai_cls.call_args[1]
timeout = call_kwargs["timeout"]
assert timeout.read == 60.0
assert timeout.connect == 20.0


class TestEmbeddingServiceTimeout:
"""embedding_service の OpenAI クライアントタイムアウト設定テスト"""

def setup_method(self):
"""各テストの前にクライアントキャッシュをリセット"""
import services.embedding_service as mod
mod._client = None

@patch("services.embedding_service.OpenAI")
def test_embedding_client_has_timeout(self, mock_openai_cls):
"""embedding_service の _get_client が httpx.Timeout を設定して OpenAI を初期化する"""
mock_openai_cls.return_value = MagicMock()

from services.embedding_service import _get_client
_get_client()

mock_openai_cls.assert_called_once()
call_kwargs = mock_openai_cls.call_args[1]
assert "timeout" in call_kwargs
timeout = call_kwargs["timeout"]
assert isinstance(timeout, httpx.Timeout)
assert timeout.read == 30.0
assert timeout.connect == 10.0

@patch("services.embedding_service.OpenAI")
def test_embedding_client_uses_config_values(self, mock_openai_cls):
"""embedding_service が config の設定値を使用してタイムアウトを設定する"""
mock_openai_cls.return_value = MagicMock()

with patch("services.embedding_service.settings") as mock_settings:
mock_settings.openai_api_key = "test-key"
mock_settings.openai_timeout = 45.0
mock_settings.openai_connect_timeout = 15.0

import services.embedding_service as mod
mod._client = None

from services.embedding_service import _get_client
_get_client()

call_kwargs = mock_openai_cls.call_args[1]
timeout = call_kwargs["timeout"]
assert timeout.read == 45.0
assert timeout.connect == 15.0


class TestConfigTimeoutDefaults:
"""config.py のタイムアウトデフォルト値テスト"""

def test_default_timeout_values(self):
"""デフォルトのタイムアウト値が正しく設定されている"""
from config import Settings
s = Settings(openai_api_key="test")
assert s.openai_timeout == 30.0
assert s.openai_connect_timeout == 10.0
Loading