032: Phase 4 US2 — Translate plugin fully async

T022-T028: All translate methods async.
- _llm_async_http.py created (httpx.AsyncClient+asyncio.sleep)
- Old _llm_http.py and preview_llm_client.py preserved (tombstone later)
- superset_executor, preview, executor, run_source, llm_call all async

RATIONALE: httpx.AsyncClient + asyncio.sleep instead of time.sleep.
REJECTED: AsyncOpenAI SDK — doesn't support custom base_url.
This commit is contained in:
2026-06-04 20:09:45 +03:00
parent 846d2cb9a9
commit e377c965e9
9 changed files with 316 additions and 73 deletions

View File

@@ -18,7 +18,7 @@ from ...core.config_manager import ConfigManager
from ...core.logger import belief_scope, logger
from ...core.superset_client import SupersetClient
from ...models.translate import TranslationJob
from .preview_llm_client import LLMClient
from ._llm_async_http import call_openai_compatible
from .preview_resolve_provider import resolve_provider_model as _resolve_provider_model
from .preview_response_parser import (
compute_config_hash as _compute_config_hash,
@@ -38,7 +38,7 @@ class PreviewExecutor:
# region fetch_sample_rows [TYPE Function]
# @PURPOSE: Fetch sample rows from Superset dataset for preview.
# @SIDE_EFFECT Calls Superset chart data endpoint.
def fetch_sample_rows(
async def fetch_sample_rows(
self,
job: TranslationJob,
sample_size: int = 10,
@@ -55,7 +55,7 @@ class PreviewExecutor:
raise ValueError("No Superset environments configured")
client = SupersetClient(env_config)
dataset_detail = client.get_dataset_detail(int(job.source_datasource_id))
dataset_detail = await client.get_dataset_detail(int(job.source_datasource_id))
query_context = client.build_dataset_preview_query_context(
dataset_id=int(job.source_datasource_id), dataset_record=dataset_detail,
template_params={}, effective_filters=[],
@@ -71,7 +71,7 @@ class PreviewExecutor:
form_data.pop("query_mode", None)
try:
response = client.network.request(
response = await client.network.request(
method="POST", endpoint="/api/v1/chart/data",
data=json.dumps(query_context),
headers={"Content-Type": "application/json"},
@@ -85,7 +85,7 @@ class PreviewExecutor:
# region call_llm [TYPE Function]
# @PURPOSE: Call the configured LLM provider with a prompt.
# @SIDE_EFFECT Makes HTTP call to LLM provider.
def call_llm(self, job: TranslationJob, prompt: str, max_tokens: int = 8192) -> str:
async def call_llm(self, job: TranslationJob, prompt: str, max_tokens: int = 8192) -> str:
with belief_scope("PreviewExecutor.call_llm"):
if not job.provider_id:
raise ValueError("Job has no LLM provider configured")
@@ -108,7 +108,7 @@ class PreviewExecutor:
max_attempts = 2
for attempt in range(max_attempts):
try:
response_text = LLMClient.call_openai_compatible(
content_text, _ = await call_openai_compatible(
base_url=provider.base_url, api_key=api_key, model=model,
prompt=prompt, provider_type=provider_type,
max_tokens=max_tokens * (attempt + 1),
@@ -121,7 +121,7 @@ class PreviewExecutor:
continue
raise
return response_text
return content_text
# endregion call_llm
# region delegation_helpers [TYPE Function]