Uh oh!
There was an error while loading. Please reload this page.
feat: add vNext engine support - #9
Conversation
📝 WalkthroughWalkthroughAdds optional Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Engine
participant Config
participant Auth
participant vNextAPI as vNext API
participant LegacyAPI as Legacy API
Client->>Engine: quick_translate(..., engine_id?)
Engine->>Config: validate api_url, engine_id
Engine->>Engine: generate _session_id, set _is_vnext
Engine->>Auth: _ensure_client() -> choose header
alt vNext (engine_id present)
Auth-->>Engine: X-API-Key header
Engine->>vNextAPI: POST /process/{engine_id}/localize {sessionId, sourceLocale, targetLocale, data, ...}
vNextAPI-->>Engine: localized response
else Legacy (no engine_id)
Auth-->>Engine: Authorization: Bearer token
Engine->>LegacyAPI: POST /i18n or /recognize {...}
LegacyAPI-->>Engine: localized response
end
Engine->>Client: return translated content
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Adds vNext API support to the Python SDK by enabling an engine_id-driven mode that switches base URL/auth headers and routes certain calls to new vNext endpoints while keeping legacy behavior unchanged when engine_id is absent.
Changes:
- Extend
EngineConfigwith optionalengine_idand auto-selecthttps://api.lingo.devas the default whenengine_idis set. - Branch authentication (
X-API-KeyvsAuthorization: Bearer) and endpoint paths for_localize_chunk,recognize_locale, andwhoami(GET/users/mefor vNext). - Add
engine_idsupport toquick_translate/quick_batch_translateand introduce unit tests covering vNext behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/lingodotdev/engine.py | Adds engine_id config, vNext detection, auth/header branching, and vNext endpoint routing. |
tests/test_engine.py | Adds unit tests validating vNext URL/body/header behavior and the default API URL switch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/lingodotdev/engine.py (1)
574-583: Update quick-helper docstrings forengine_id.
quick_translateandquick_batch_translatenow acceptengine_id, but the Args sections don’t document it yet.Also applies to: 636-645
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lingodotdev/engine.py` around lines 574 - 583, Update the docstrings for both quick_translate and quick_batch_translate to document the new engine_id parameter: add an Args entry describing engine_id (type Optional[str], purpose to override/select the specific engine instance, default behavior when None) and include any notes about compatibility with api_url or fast flags; ensure the parameter name matches the function signatures (engine_id) and appears in the Args sections near api_url/fast so readers can find it easily.tests/test_engine.py (1)
646-677: Add a regression test for empty/whitespaceengine_id.The new suite covers vNext happy paths well, but it should also assert that invalid values like
""/" "are rejected to prevent malformed vNext routing/auth behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_engine.py` around lines 646 - 677, Add regression tests for empty/whitespace engine_id by creating LingoDotDevEngine instances with engine_id = "" and engine_id = " " and asserting these are treated as invalid: verify engine._is_vnext is False, engine.config.engine_id is falsy/normalized (e.g. None or empty after stripping), and the api_url is not switched to "https://api.lingo.dev" (i.e. remains the non-vNext/default value); add these as new test methods alongside TestVNextEngine to prevent malformed vNext routing/auth behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lingodotdev/engine.py`:
- Around line 220-223: The vNext URL assembly uses f-strings with
self.config.api_url which can end with a trailing slash and produce double
slashes; fix by normalizing once before concatenation (e.g., base_api =
self.config.api_url.rstrip("/")) and then build URLs with
f"{base_api}/process/{self.config.engine_id}/localize" (and similarly for other
vNext endpoints). Update the occurrences where url is created under
self._is_vnext (including the blocks around the current url in the method using
self._is_vnext and the other two locations mentioned) to use the normalized
base_api variable so no double slashes are produced.
- Line 21: Reject empty-string engine_id values and make vNext detection
truthy-based: add validation where engine_id is set (e.g., in the Engine
dataclass __post_init__ or the constructor that assigns engine_id) to raise an
error if engine_id is None or engine_id.strip() == "" (use ValueError) so blank
strings don't pass config, and change the _is_vnext check to use a truthiness
test (e.g., return bool(self.engine_id) or bool(self.engine_id and
self.engine_id.strip())) instead of comparison to None so empty strings are
treated as falsy and will not produce malformed paths like /process//localize;
update all places referencing engine_id and _is_vnext accordingly.
In `@tests/test_engine.py`:
- Line 675: The file tests/test_engine.py fails Black formatting; run the
formatter (e.g., execute `black --write .` or `black tests/test_engine.py`) and
commit the changes so the instantiation line `engine =
LingoDotDevEngine({"api_key": "key", "api_url": "https://api.test.com"})` and
the other affected ranges (around lines with that instantiation and the blocks
at 693 and 708-710) are reformatted to comply with Black; ensure you run Black
locally, stage the modified file(s), and push the commit to unblock the CI.
---
Nitpick comments:
In `@src/lingodotdev/engine.py`:
- Around line 574-583: Update the docstrings for both quick_translate and
quick_batch_translate to document the new engine_id parameter: add an Args entry
describing engine_id (type Optional[str], purpose to override/select the
specific engine instance, default behavior when None) and include any notes
about compatibility with api_url or fast flags; ensure the parameter name
matches the function signatures (engine_id) and appears in the Args sections
near api_url/fast so readers can find it easily.
In `@tests/test_engine.py`:
- Around line 646-677: Add regression tests for empty/whitespace engine_id by
creating LingoDotDevEngine instances with engine_id = "" and engine_id = " "
and asserting these are treated as invalid: verify engine._is_vnext is False,
engine.config.engine_id is falsy/normalized (e.g. None or empty after
stripping), and the api_url is not switched to "https://api.lingo.dev" (i.e.
remains the non-vNext/default value); add these as new test methods alongside
TestVNextEngine to prevent malformed vNext routing/auth behavior.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lingodotdev/engine.py (1)
596-603:⚠️ Potential issue | 🟡 MinorDocument
engine_idin quick helperArgssections.
Line 590andLine 652addengine_id, but the docstrings still omit it.Docstring patch
Args: content: Text string or dict to translate api_key: Your Lingo.dev API key target_locale: Target language code (e.g., 'es', 'fr') source_locale: Source language code (optional, auto-detected if None) api_url: API endpoint URL fast: Enable fast mode for quicker translations + engine_id: Optional vNext engine ID (enables vNext routing when provided)Args: content: Text string or dict to translate api_key: Your Lingo.dev API key target_locales: List of target language codes (e.g., ['es', 'fr', 'de']) source_locale: Source language code (optional, auto-detected if None) api_url: API endpoint URL fast: Enable fast mode for quicker translations + engine_id: Optional vNext engine ID (enables vNext routing when provided)Also applies to: 658-665
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lingodotdev/engine.py` around lines 596 - 603, The docstrings for the quick helper(s) omitted the newly added engine_id parameter; update the Args sections for the quick helper functions that now accept engine_id to include a brief description for the engine_id parameter (e.g., "engine_id: ID of the translation engine to use (optional); defaults to the account's default engine"), ensuring you add the engine_id entry alongside the existing entries (content, api_key, target_locale, source_locale, api_url, fast) so both occurrences of the quick helper docstrings mention engine_id.
🧹 Nitpick comments (2)
src/lingodotdev/engine.py (1)
34-45: Normalize first, then apply default-host vNext switching.
Line 37checks for the default URL before trailing-slash normalization. Withengine_idset,api_url="https://engine.lingo.dev/"won’t switch tohttps://api.lingo.dev, even though it is the same default host form.Proposed adjustment
`@validator`("api_url", pre=True, always=True) `@classmethod` def validate_api_url(cls, v: Optional[str], values: Dict[str, Any]) -> str: - if v is None or v == "https://engine.lingo.dev":- engine_id = values.get("engine_id")- if engine_id:- return "https://api.lingo.dev"- if v is None:- return "https://engine.lingo.dev"- if not v.startswith(("http://", "https://")):+ normalized = (v or "https://engine.lingo.dev").strip().rstrip("/")+ if not normalized.startswith(("http://", "https://")): raise ValueError("API URL must be a valid HTTP/HTTPS URL") - return v.rstrip("/")+ if values.get("engine_id") and normalized == "https://engine.lingo.dev":+ return "https://api.lingo.dev"+ return normalized🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lingodotdev/engine.py` around lines 34 - 45, The validator validate_api_url should normalize trailing slashes before comparing against the default host so that values like "https://engine.lingo.dev/" are treated the same as "https://engine.lingo.dev"; modify validate_api_url to strip a trailing "/" from v (if v is not None) at the start, then perform the engine_id-based host switch to "https://api.lingo.dev", then validate scheme with startswith and finally return the normalized value; reference api_url, validate_api_url, and engine_id when locating the code to change.tests/test_engine.py (1)
675-685: Add regression coverage for default legacy URL with trailing slash +engine_id.You cover custom trailing-slash normalization; add the canonical default case (
"https://engine.lingo.dev/") to lock expected vNext host switching behavior.Suggested test
def test_api_url_trailing_slash_stripped(self): """Test that trailing slash is stripped from api_url""" engine = LingoDotDevEngine( { "api_key": "key", "engine_id": "eng", "api_url": "https://custom.api.com/", } ) assert engine.config.api_url == "https://custom.api.com" + def test_default_legacy_url_with_trailing_slash_switches_to_vnext_host(self):+ """Default legacy URL form with trailing slash should still route to api.lingo.dev."""+ engine = LingoDotDevEngine(+ {+ "api_key": "key",+ "engine_id": "eng",+ "api_url": "https://engine.lingo.dev/",+ }+ )+ assert engine.config.api_url == "https://api.lingo.dev"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_engine.py` around lines 675 - 685, Add a regression test that mirrors test_api_url_trailing_slash_stripped but uses the legacy default URL "https://engine.lingo.dev/" plus an engine_id to ensure trailing-slash normalization and the default vNext host behavior; create a new test (e.g., test_default_legacy_url_trailing_slash_with_engine_id) that constructs LingoDotDevEngine with {"api_key":"key","engine_id":"eng","api_url":"https://engine.lingo.dev/"} and asserts engine.config.api_url == "https://engine.lingo.dev" (or the expected vNext host if the code should transform it), using the LingoDotDevEngine constructor and config.api_url symbol references to locate the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/lingodotdev/engine.py`:
- Around line 596-603: The docstrings for the quick helper(s) omitted the newly
added engine_id parameter; update the Args sections for the quick helper
functions that now accept engine_id to include a brief description for the
engine_id parameter (e.g., "engine_id: ID of the translation engine to use
(optional); defaults to the account's default engine"), ensuring you add the
engine_id entry alongside the existing entries (content, api_key, target_locale,
source_locale, api_url, fast) so both occurrences of the quick helper docstrings
mention engine_id.
---
Nitpick comments:
In `@src/lingodotdev/engine.py`:
- Around line 34-45: The validator validate_api_url should normalize trailing
slashes before comparing against the default host so that values like
"https://engine.lingo.dev/" are treated the same as "https://engine.lingo.dev";
modify validate_api_url to strip a trailing "/" from v (if v is not None) at the
start, then perform the engine_id-based host switch to "https://api.lingo.dev",
then validate scheme with startswith and finally return the normalized value;
reference api_url, validate_api_url, and engine_id when locating the code to
change.
In `@tests/test_engine.py`:
- Around line 675-685: Add a regression test that mirrors
test_api_url_trailing_slash_stripped but uses the legacy default URL
"https://engine.lingo.dev/" plus an engine_id to ensure trailing-slash
normalization and the default vNext host behavior; create a new test (e.g.,
test_default_legacy_url_trailing_slash_with_engine_id) that constructs
LingoDotDevEngine with
{"api_key":"key","engine_id":"eng","api_url":"https://engine.lingo.dev/"} and
asserts engine.config.api_url == "https://engine.lingo.dev" (or the expected
vNext host if the code should transform it), using the LingoDotDevEngine
constructor and config.api_url symbol references to locate the logic.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_engine.py (1)
655-658: Useasyncio.run()for async cleanup to avoid deprecation warning.
asyncio.get_event_loop()is deprecated in Python 3.10+ and will raise an error in Python 3.12+ when there is no current event loop. Replace withasyncio.run(), which is the recommended pattern for synchronous code that needs to run async operations.♻️ Suggested fix
def teardown_method(self): """Clean up engine client""" if self.engine._client and not self.engine._client.is_closed: - asyncio.get_event_loop().run_until_complete(self.engine.close())+ asyncio.run(self.engine.close())🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_engine.py` around lines 655 - 658, The teardown_method currently uses asyncio.get_event_loop().run_until_complete to close the engine client, which triggers deprecation warnings; update teardown_method so that when self.engine._client exists and is not closed you call asyncio.run(self.engine.close()) instead of get_event_loop().run_until_complete, keeping the same condition around self.engine._client and self.engine._client.is_closed and invoking the existing engine.close() coroutine.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/test_engine.py`:
- Around line 655-658: The teardown_method currently uses
asyncio.get_event_loop().run_until_complete to close the engine client, which
triggers deprecation warnings; update teardown_method so that when
self.engine._client exists and is not closed you call
asyncio.run(self.engine.close()) instead of get_event_loop().run_until_complete,
keeping the same condition around self.engine._client and
self.engine._client.is_closed and invoking the existing engine.close()
coroutine.
Description
Add vNext API support to the Python SDK. When engine_id is provided in config, the SDK uses the new api.lingo.dev API with X-API-Key auth and vNext endpoint paths. When engine_id is not provided, the SDK continues to use the legacy engine.lingo.dev API with Bearer token auth. All existing methods work transparently with both APIs.
Type of Change
Changes:
Testing
Checklist
Summary by CodeRabbit
New Features
Tests