Skip to content

feat: vNext migration#10

Open
AndreyHirsa wants to merge 7 commits intomainfrom
feat/v-next
Open

feat: vNext migration#10
AndreyHirsa wants to merge 7 commits intomainfrom
feat/v-next

Conversation

@AndreyHirsa
Copy link

@AndreyHirsa AndreyHirsa commented Mar 4, 2026

Description

Consolidate localization to a single /process/localize endpoint with engineId passed in the request body. The engine_id is now an optional configuration option.

Key changes:

  • engine_id is an optional field in EngineConfig, quick_translate, and quick_batch_translate (defaults to None)
  • Single localization endpoint: /process/localize (instead of /process/{engine_id}/localize)
  • engineId is included in the request body only when provided
  • Empty or whitespace-only engine_id values normalize to None
  • Default api_url is https://api.lingo.dev
  • Auth uses X-API-Key header
  • Request format uses sourceLocale/targetLocale fields with sessionId
  • Updated all tests, documentation, and examples

Type of Change

  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Testing

  • Tests pass locally
  • New tests added for new functionality
  • Integration tests pass

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Added an optional engine_id parameter to quick_translate and quick_batch_translate for selecting an engine.
  • Documentation

    • README and examples updated to show engine_id usage across configuration and operation examples.
  • Breaking Changes

    • API interactions now use session-based tracking and updated endpoint shapes.
    • Authentication behavior changed to use a dedicated API key header format.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72fe7b4f-c681-467c-99f5-9f4d9a91d964

📥 Commits

Reviewing files that changed from the base of the PR and between 9f58f69 and 5c0a9cf.

📒 Files selected for processing (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

📝 Walkthrough

Walkthrough

Adds an optional engine_id to configuration and public quick-translate APIs; switches auth header to X-API-Key, introduces per-instance sessionId, and updates endpoint paths/methods and payload shapes to use /process/... and /users/... (payloads include engineId/sessionId).

Changes

Cohort / File(s) Summary
Docs / Examples
README.md
Examples and docs updated to surface engine_id in configuration and in calls to quick_translate/quick_batch_translate; API defaults and usage examples adjusted.
Engine core & client
src/lingodotdev/engine.py
Added engine_id to EngineConfig with validation; trim trailing slash from api_url; generate per-instance sessionId; switch auth header to X-API-Key; change endpoints and payload shapes (e.g., POST /process/localize, POST /process/recognize, GET /users/whoami); include engineId/sessionId in payloads; updated quick_translate/quick_batch_translate signatures to accept and propagate engine_id.
Unit tests
tests/test_engine.py
Tests updated to construct EngineConfig with engine_id, validate trimming/whitespace rules and api_url interplay, assert X-API-Key header and sessionId generation, and verify new endpoint paths/methods and payload fields.
Integration tests
tests/test_integration.py
Real and mocked integration tests updated to optionally include engine_id (from env var when present), assert sessionId consistency (renamed from workflowId), and adapt to new request body/endpoint expectations.

Sequence Diagram

sequenceDiagram
    participant Client
    participant EngineConfig
    participant APIClient
    participant APIServer as "API Server"

    Client->>EngineConfig: quick_translate(content, api_key, engine_id, target_locale)
    EngineConfig->>EngineConfig: validate engine_id\ntrim api_url\ngenerate sessionId
    EngineConfig->>APIClient: init with api_url, engine_id, sessionId, api_key
    APIClient->>APIServer: POST /process/localize\nHeaders: X-API-Key\nPayload: {engineId, sourceLocale, targetLocale, data, sessionId}
    APIServer-->>APIClient: translated content
    APIClient-->>Client: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibbled a new engine_id,

sessionId tucked in my hop,
X-API-Key jingles bright,
endpoints moved to the process path,
translations hop back on time.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: vNext migration' is vague and generic, using a non-specific term 'vNext migration' that does not convey meaningful information about the substantial API changes made in the changeset. Consider a more descriptive title like 'feat: add engine_id support and consolidate localization endpoints' to better communicate the main changes at a glance.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 96.30% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/v-next

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/test_engine.py (1)

40-43: Use a narrower expected exception in test_engine_id_required.

Catching Exception here is too broad and can hide unrelated failures. Tightening the expected exception type will make this test more trustworthy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_engine.py` around lines 40 - 43, The test test_engine_id_required
should assert a specific exception type instead of Exception; change the context
manager from pytest.raises(Exception, match="engine_id") to a narrower exception
that EngineConfig actually raises (e.g., pytest.raises(TypeError,
match="engine_id") if EngineConfig.__init__ enforces a missing positional arg,
or pytest.raises(ValueError, match="engine_id") if it validates and raises
ValueError) so update the pytest.raises call in test_engine_id_required to the
concrete exception type returned by EngineConfig.
🤖 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 205-212: The URL is built with raw self.config.engine_id which can
contain reserved characters; URL-encode the engine_id before composing url
(e.g., use urllib.parse.quote with safe='' on self.config.engine_id) and replace
direct insertion in the f-string with the encoded value, adding the necessary
import if missing; update the url assignment that currently uses
f"{self.config.api_url}/process/{self.config.engine_id}/localize" to use the
encoded engine_id variable instead.
- Around line 23-35: The validators validate_engine_id and validate_api_url call
string methods directly and can raise AttributeError for non-string inputs;
update them to first check the type and presence (e.g., ensure v is not None and
isinstance(v, str)) before using strip(), startswith(), or rstrip(), raise the
existing ValueError with the same messages for invalid or missing string values,
and return the cleaned string (strip()/rstrip("/")) when valid; reference the
validator methods validate_engine_id (for engine_id) and validate_api_url (for
api_url).

In `@tests/test_engine.py`:
- Around line 646-762: CI reports a Black formatting failure; run the Black
formatter over the repo (e.g., black .) and commit the updated formatting so the
tests in tests/test_engine.py (e.g., test_localize_chunk_url_and_body,
test_recognize_locale_url, test_full_localization_workflow) follow Black style;
then push the commit and re-run CI to confirm the Black check passes.

In `@tests/test_integration.py`:
- Around line 343-353: The test currently assumes mock_post.call_args_list
produced at least one call before indexing session_ids[0]; add an explicit
assertion before that access (e.g., assert len(session_ids) > 0 or assert
mock_post.called) with a clear message like "expected at least one POST call" so
failures are explicit, then keep the existing uniqueness and non-empty string
assertions; reference session_ids and mock_post.call_args_list when locating the
change.

---

Nitpick comments:
In `@tests/test_engine.py`:
- Around line 40-43: The test test_engine_id_required should assert a specific
exception type instead of Exception; change the context manager from
pytest.raises(Exception, match="engine_id") to a narrower exception that
EngineConfig actually raises (e.g., pytest.raises(TypeError, match="engine_id")
if EngineConfig.__init__ enforces a missing positional arg, or
pytest.raises(ValueError, match="engine_id") if it validates and raises
ValueError) so update the pytest.raises call in test_engine_id_required to the
concrete exception type returned by EngineConfig.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e17f2f9-8874-4ffd-a922-86d752689270

📥 Commits

Reviewing files that changed from the base of the PR and between 70c961d and 13d8efd.

📒 Files selected for processing (4)
  • README.md
  • src/lingodotdev/engine.py
  • tests/test_engine.py
  • tests/test_integration.py

Comment on lines +343 to 353
# Extract session IDs from all calls
session_ids = []
for call in mock_post.call_args_list:
request_data = call[1]["json"]
workflow_id = request_data["params"]["workflowId"]
workflow_ids.append(workflow_id)
session_id = request_data["sessionId"]
session_ids.append(session_id)

# All workflow IDs should be the same
assert len(set(workflow_ids)) == 1
assert len(workflow_ids[0]) > 0 # Should be a non-empty string
# All session IDs should be the same
assert len(set(session_ids)) == 1
assert len(session_ids[0]) > 0 # Should be a non-empty string

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against empty call list before indexing session_ids[0].

At Line 352, indexing assumes at least one request was made. Add an explicit assertion first so regressions fail with a clear message.

Suggested test hardening
         # All session IDs should be the same
+        assert session_ids, "Expected at least one localization request"
         assert len(set(session_ids)) == 1
         assert len(session_ids[0]) > 0  # Should be a non-empty string
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Extract session IDs from all calls
session_ids = []
for call in mock_post.call_args_list:
request_data = call[1]["json"]
workflow_id = request_data["params"]["workflowId"]
workflow_ids.append(workflow_id)
session_id = request_data["sessionId"]
session_ids.append(session_id)
# All workflow IDs should be the same
assert len(set(workflow_ids)) == 1
assert len(workflow_ids[0]) > 0 # Should be a non-empty string
# All session IDs should be the same
assert len(set(session_ids)) == 1
assert len(session_ids[0]) > 0 # Should be a non-empty string
# Extract session IDs from all calls
session_ids = []
for call in mock_post.call_args_list:
request_data = call[1]["json"]
session_id = request_data["sessionId"]
session_ids.append(session_id)
# All session IDs should be the same
assert session_ids, "Expected at least one localization request"
assert len(set(session_ids)) == 1
assert len(session_ids[0]) > 0 # Should be a non-empty string
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_integration.py` around lines 343 - 353, The test currently assumes
mock_post.call_args_list produced at least one call before indexing
session_ids[0]; add an explicit assertion before that access (e.g., assert
len(session_ids) > 0 or assert mock_post.called) with a clear message like
"expected at least one POST call" so failures are explicit, then keep the
existing uniqueness and non-empty string assertions; reference session_ids and
mock_post.call_args_list when locating the change.

@AndreyHirsa AndreyHirsa marked this pull request as draft March 4, 2026 22:48
@AndreyHirsa AndreyHirsa marked this pull request as ready for review March 5, 2026 19:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
tests/test_integration.py (1)

349-351: ⚠️ Potential issue | 🟡 Minor

Add an explicit non-empty assertion before session_ids[0] access.

This still assumes at least one mocked POST call. If no calls are made, the test fails with IndexError instead of a clear failure reason.

💡 Suggested hardening
         # All session IDs should be the same
+        assert session_ids, "Expected at least one localization request"
         assert len(set(session_ids)) == 1
         assert len(session_ids[0]) > 0  # Should be a non-empty string
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_integration.py` around lines 349 - 351, The test currently
accesses session_ids[0] without verifying session_ids contains any entries; add
an explicit non-empty check (e.g., assert len(session_ids) > 0 or assert
session_ids) before the existing assertions so the failure is a clear assertion
error instead of IndexError when no mocked POSTs occurred; update the assertions
around session_ids and keep the subsequent checks (len(set(session_ids)) == 1
and len(session_ids[0]) > 0) unchanged.
src/lingodotdev/engine.py (1)

25-33: ⚠️ Potential issue | 🟡 Minor

Harden engine_id validator to handle non-string inputs gracefully.

The current validator calls v.strip() on line 30 without a type check. With pre=True, non-string inputs (e.g., EngineConfig(engine_id=123)) will reach the validator before Pydantic's type validation, causing an AttributeError: 'int' object has no attribute 'strip' instead of a clean validation error. Add a type check to convert unexpected input types to a clear ValueError.

🛠️ Suggested fix
     `@validator`("engine_id", pre=True, always=True)
     `@classmethod`
     def validate_engine_id(cls, v: Optional[str]) -> Optional[str]:
         if v is None:
             return None
+        if not isinstance(v, str):
+            raise ValueError("engine_id must be a string")
         stripped = v.strip()
         if not stripped:
             return None
         return stripped
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lingodotdev/engine.py` around lines 25 - 33, The validator
validate_engine_id in the class (decorated with `@validator`("engine_id",
pre=True, always=True)) currently calls v.strip() without checking v's type,
which raises AttributeError for non-string inputs; update the validator to first
check whether v is None, then if isinstance(v, str) use v.strip() and return
None for empty strings, else raise a ValueError (or return None) with a clear
message indicating engine_id must be a string so non-string inputs like integers
produce a clean validation error rather than an AttributeError.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 225-226: The README entries for quick_translate and
quick_batch_translate list parameters in the wrong order; update the doc
signatures for the convenience methods quick_translate(...) and
quick_batch_translate(...) so their parameter order exactly matches the actual
function signatures in code (verify the real order in the implementation and
reorder the listed parameters—e.g., swap content/api_key or
target_locale/target_locales positions as necessary) to prevent misleading
positional calls.

---

Duplicate comments:
In `@src/lingodotdev/engine.py`:
- Around line 25-33: The validator validate_engine_id in the class (decorated
with `@validator`("engine_id", pre=True, always=True)) currently calls v.strip()
without checking v's type, which raises AttributeError for non-string inputs;
update the validator to first check whether v is None, then if isinstance(v,
str) use v.strip() and return None for empty strings, else raise a ValueError
(or return None) with a clear message indicating engine_id must be a string so
non-string inputs like integers produce a clean validation error rather than an
AttributeError.

In `@tests/test_integration.py`:
- Around line 349-351: The test currently accesses session_ids[0] without
verifying session_ids contains any entries; add an explicit non-empty check
(e.g., assert len(session_ids) > 0 or assert session_ids) before the existing
assertions so the failure is a clear assertion error instead of IndexError when
no mocked POSTs occurred; update the assertions around session_ids and keep the
subsequent checks (len(set(session_ids)) == 1 and len(session_ids[0]) > 0)
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bac78be6-9b7b-4b39-afba-044ce78b81aa

📥 Commits

Reviewing files that changed from the base of the PR and between f1d3f4f and 9f58f69.

📒 Files selected for processing (4)
  • README.md
  • src/lingodotdev/engine.py
  • tests/test_engine.py
  • tests/test_integration.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant