Skip to content

Commit ab93eff

Browse files
Merge pull request #52 from vbossica/move_translation_method
Move the _get_*_response methods inside the provider classes
2 parents f43fc40 + 68cb224 commit ab93eff

15 files changed

+237
-132
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies = [
3131
"anthropic==0.48.0",
3232
"requests==2.32.3",
3333
"responses==0.25.6",
34+
"isort==6.0.1",
3435
]
3536
classifiers = [
3637
"Development Status :: 5 - Production/Stable",

python_gpt_po/services/providers/anthropic_provider.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,16 @@ def get_fallback_models(self) -> List[str]:
6262
"claude-3-5-sonnet-latest",
6363
"claude-3-opus-20240229",
6464
]
65+
66+
def translate(self, provider_clients: ProviderClients, model: str, content: str) -> str:
67+
"""Get response from Anthropic API."""
68+
if not self.is_client_initialized(provider_clients):
69+
raise ValueError("Anthropic client not initialized")
70+
71+
message = {"role": "user", "content": content}
72+
completion = provider_clients.anthropic_client.messages.create(
73+
model=model,
74+
max_tokens=4000,
75+
messages=[message]
76+
)
77+
return completion.content[0].text.strip()

python_gpt_po/services/providers/azure_openai_provider.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,16 @@ def is_client_initialized(self, provider_clients: ProviderClients) -> bool:
4545
def get_fallback_models(self) -> List[str]:
4646
"""Get fallback models for Azure OpenAI."""
4747
return ["gpt-35-turbo", "gpt-4"]
48+
49+
def translate(self, provider_clients: ProviderClients, model: str, content: str) -> str:
50+
"""Get response from OpenAI API."""
51+
if not self.is_client_initialized(provider_clients):
52+
raise ValueError("OpenAI client not initialized")
53+
54+
message = {"role": "user", "content": content}
55+
completion = provider_clients.azure_openai_client.chat.completions.create(
56+
model=model,
57+
max_tokens=4000,
58+
messages=[message]
59+
)
60+
return completion.choices[0].message.content.strip()

python_gpt_po/services/providers/base.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,16 @@ def get_fallback_models(self) -> List[str]:
5858
List of fallback model IDs
5959
"""
6060
return []
61+
62+
@abstractmethod
63+
def translate(self, provider_clients: ProviderClients, model: str, content: str) -> str:
64+
"""Translate content using the specified model.
65+
66+
Args:
67+
provider_clients: Provider clients instance
68+
model: Model to use for translation
69+
content: Content to translate
70+
71+
Returns:
72+
Translated content
73+
"""

python_gpt_po/services/providers/deepseek_provider.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,26 @@ def is_client_initialized(self, provider_clients: ProviderClients) -> bool:
5454
def get_fallback_models(self) -> List[str]:
5555
"""Get fallback models for DeepSeek."""
5656
return ["deepseek-chat", "deepseek-coder"]
57+
58+
def translate(self, provider_clients: ProviderClients, model: str, content: str) -> str:
59+
"""Get response from DeepSeek API."""
60+
if not self.is_client_initialized(provider_clients):
61+
raise ValueError("DeepSeek client not initialized")
62+
63+
headers = {
64+
"Authorization": f"Bearer {provider_clients.deepseek_api_key}",
65+
"Content-Type": "application/json"
66+
}
67+
payload = {
68+
"model": model,
69+
"messages": [{"role": "user", "content": content}],
70+
"max_tokens": 4000
71+
}
72+
response = requests.post(
73+
f"{provider_clients.deepseek_base_url}/chat/completions",
74+
headers=headers,
75+
json=payload,
76+
timeout=30
77+
)
78+
response.raise_for_status()
79+
return response.json()["choices"][0]["message"]["content"].strip()

python_gpt_po/services/providers/openai_provider.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,15 @@ def get_fallback_models(self) -> List[str]:
5151
"gpt-4",
5252
"gpt-3.5-turbo"
5353
]
54+
55+
def translate(self, provider_clients: ProviderClients, model: str, content: str) -> str:
56+
"""Get response from OpenAI API."""
57+
if not self.is_client_initialized(provider_clients):
58+
raise ValueError("OpenAI client not initialized")
59+
60+
message = {"role": "user", "content": content}
61+
completion = provider_clients.openai_client.chat.completions.create(
62+
model=model,
63+
messages=[message]
64+
)
65+
return completion.choices[0].message.content.strip()

python_gpt_po/services/translation_service.py

Lines changed: 8 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
from typing import Any, Dict, List, Optional
1111

1212
import polib
13-
import requests
1413
from tenacity import retry, stop_after_attempt, wait_fixed
1514

1615
from ..models.config import TranslationConfig
17-
from ..models.enums import ModelProvider
1816
from .model_manager import ModelManager
1917
from .po_file_handler import POFileHandler
18+
from .providers.registry import ProviderRegistry
2019

2120

2221
class TranslationService:
@@ -35,67 +34,6 @@ def __init__(self, config: TranslationConfig, batch_size: int = 40):
3534
self.po_file_handler = POFileHandler()
3635
self.model_manager = ModelManager()
3736

38-
def _get_openai_response(self, content: str) -> str:
39-
"""Get response from OpenAI API."""
40-
if not self.config.provider_clients.openai_client:
41-
raise ValueError("OpenAI client not initialized")
42-
43-
message = {"role": "user", "content": content}
44-
completion = self.config.provider_clients.openai_client.chat.completions.create(
45-
model=self.config.model,
46-
messages=[message]
47-
)
48-
return completion.choices[0].message.content.strip()
49-
50-
def _get_anthropic_response(self, content: str) -> str:
51-
"""Get response from Anthropic API."""
52-
if not self.config.provider_clients.anthropic_client:
53-
raise ValueError("Anthropic client not initialized")
54-
55-
message = {"role": "user", "content": content}
56-
completion = self.config.provider_clients.anthropic_client.messages.create(
57-
model=self.config.model,
58-
max_tokens=4000,
59-
messages=[message]
60-
)
61-
return completion.content[0].text.strip()
62-
63-
def _get_deepseek_response(self, content: str) -> str:
64-
"""Get response from DeepSeek API."""
65-
if not self.config.provider_clients.deepseek_api_key:
66-
raise ValueError("DeepSeek API key not set")
67-
68-
headers = {
69-
"Authorization": f"Bearer {self.config.provider_clients.deepseek_api_key}",
70-
"Content-Type": "application/json"
71-
}
72-
payload = {
73-
"model": self.config.model,
74-
"messages": [{"role": "user", "content": content}],
75-
"max_tokens": 4000
76-
}
77-
response = requests.post(
78-
f"{self.config.provider_clients.deepseek_base_url}/chat/completions",
79-
headers=headers,
80-
json=payload,
81-
timeout=30
82-
)
83-
response.raise_for_status()
84-
return response.json()["choices"][0]["message"]["content"].strip()
85-
86-
def _get_azure_openai_response(self, content: str) -> str:
87-
"""Get response from OpenAI API."""
88-
if not self.config.provider_clients.azure_openai_client:
89-
raise ValueError("OpenAI client not initialized")
90-
91-
message = {"role": "user", "content": content}
92-
completion = self.config.provider_clients.azure_openai_client.chat.completions.create(
93-
model=self.config.model,
94-
max_tokens=4000,
95-
messages=[message]
96-
)
97-
return completion.choices[0].message.content.strip()
98-
9937
def validate_provider_connection(self) -> bool:
10038
"""Validates the connection to the selected provider by making a test API call."""
10139
provider = self.config.provider
@@ -238,15 +176,13 @@ def _get_provider_response(self, content: str) -> str:
238176
"""Get translation response from the selected provider."""
239177
provider = self.config.provider
240178

241-
if provider == ModelProvider.OPENAI:
242-
return self._get_openai_response(content)
243-
if provider == ModelProvider.ANTHROPIC:
244-
return self._get_anthropic_response(content)
245-
if provider == ModelProvider.DEEPSEEK:
246-
return self._get_deepseek_response(content)
247-
if provider == ModelProvider.AZURE_OPENAI:
248-
return self._get_azure_openai_response(content)
249-
return ""
179+
if not provider:
180+
return ""
181+
182+
provider_instance = ProviderRegistry.get_provider(provider)
183+
if not provider_instance:
184+
return ""
185+
return provider_instance.translate(self.config.provider_clients, self.config.model, content)
250186

251187
def _process_bulk_response(self, response_text: str, original_texts: List[str]) -> List[str]:
252188
"""Process a bulk translation response."""

python_gpt_po/tests/providers/__init__.py

Whitespace-only changes.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from unittest.mock import MagicMock
2+
3+
import pytest
4+
5+
from python_gpt_po.models.provider_clients import ProviderClients
6+
from python_gpt_po.services.providers.anthropic_provider import AnthropicProvider
7+
8+
9+
@pytest.fixture
10+
def mock_provider_clients() -> ProviderClients:
11+
"""Mock provider clients for testing."""
12+
clients = ProviderClients()
13+
clients.anthropic_client = MagicMock()
14+
clients.anthropic_client.api_key = "sk-ant-mock-key"
15+
return clients
16+
17+
def test_translate(mock_provider_clients: ProviderClients) -> None:
18+
"""Test bulk translation with Anthropic."""
19+
# Setup mock response
20+
mock_chatcompletion = MagicMock()
21+
mock_chatcompletion.content = [MagicMock()]
22+
mock_chatcompletion.content[0].text = '["Bonjour", "Monde", "Bienvenue dans notre application", "Au revoir"]'
23+
mock_provider_clients.anthropic_client.messages.create.return_value = mock_chatcompletion
24+
25+
provider = AnthropicProvider()
26+
translations = provider.translate(
27+
provider_clients=mock_provider_clients,
28+
model="gpt-4",
29+
content="['Hello', 'World', 'Welcome to our application', 'Goodbye']"
30+
)
31+
32+
assert translations == '["Bonjour", "Monde", "Bienvenue dans notre application", "Au revoir"]'
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from unittest.mock import MagicMock
2+
3+
import pytest
4+
5+
from python_gpt_po.models.provider_clients import ProviderClients
6+
from python_gpt_po.services.providers.azure_openai_provider import AzureOpenAIProvider
7+
8+
9+
@pytest.fixture
10+
def mock_provider_clients() -> ProviderClients:
11+
"""Mock provider clients for testing."""
12+
clients = ProviderClients()
13+
clients.azure_openai_client = MagicMock()
14+
clients.azure_openai_client.api_key = "sk-aoi-mock-key"
15+
return clients
16+
17+
def test_translate(mock_provider_clients: ProviderClients) -> None:
18+
"""Test bulk translation with Azure OpenAI."""
19+
# Setup mock response
20+
mock_chatcompletion = MagicMock()
21+
mock_chatcompletion.choices = [MagicMock()]
22+
mock_chatcompletion.choices[0].message.content = '["Bonjour", "Monde", "Bienvenue dans notre application", "Au revoir"]'
23+
mock_provider_clients.azure_openai_client.chat.completions.create.return_value = mock_chatcompletion
24+
25+
provider = AzureOpenAIProvider()
26+
translations = provider.translate(
27+
provider_clients=mock_provider_clients,
28+
model="gpt-4",
29+
content="['Hello', 'World', 'Welcome to our application', 'Goodbye']"
30+
)
31+
32+
assert translations == '["Bonjour", "Monde", "Bienvenue dans notre application", "Au revoir"]'

0 commit comments

Comments
 (0)