Commit Graph

117 Commits

Author SHA1 Message Date
d2b53c2a79 feat(validation): v2 LLM dashboard validation — plugin, routes, services
Core implementation of the v2 LLM dashboard validation pipeline:
- LLM plugin with Path A (screenshots) and Path B (logs-only) execution
- Validation task management (CRUD, schedule, run)
- WebSocket task progress with Python 3.13 asyncio fix
- Cross-task runs listing (GET /validation-tasks/runs/all)
- RecordResponse schema for validation records
- JSON prompt helper, per-dashboard status aggregation
- Prompt templates with docs/git-commit/validation presets
- Migration: v2 validation models + description column
- Tests: plugin persistence, prompt templates, batch, payload, url
2026-05-31 22:32:20 +03:00
5e5f958eaa feat(llm): auto-detect max images per request for LLM providers
Add binary-search probe endpoint POST /providers/{id}/probe-max-images
that discovers the per-request image limit by sending incrementally
more 1x1 JPEGs via the provider's own API. Result is cached in the
new max_images column on the provider config.

- LLMProviderConfig: add max_images: int | None
- LLMProvider (SQLAlchemy): add max_images column
- Migration ed28d34edde7: clean ADD COLUMN
- LLMProviderService: create/update/set_max_images
- POST /providers/{id}/probe-max-images: binary search + error parsing
- ProviderConfig.svelte: 'Detect' button in edit modal + HelpTooltip
- i18n (en/ru): 11 new keys for probe UI
2026-05-31 22:31:36 +03:00
18f88a6928 fix(translate): repair dialect detection — UPSERT routing, whitespace, MySQL quoting, dead code
CRITICAL BUG-1: UPSERT routing generated invalid ON CONFLICT for MySQL,
MSSQL, Snowflake, Oracle, DuckDB. Now uses explicit UPSERT_SUPPORTED_DIALECTS
({'postgresql', 'redshift'}) — unknown dialects get plain INSERT.

BUG-2: _extract_dialect did not strip whitespace, inconsistent with
get_dialect_from_database. Fixed guard + normalized value.

BUG-3: Whitespace-only input ('   ', '\n') returned itself instead of 'unknown'.

BUG-4: Dead code — removed 'greenplum' from POSTGRESQL_DIALECTS
(always normalized to 'postgresql').

BUG-5: MySQL identifier quoting used double quotes instead of native backticks.
Added BACKTICK_DIALECTS set.

MOCK-FIX: test_at_least_one_row_per_batch patched wrong path
(executor.estimate_token_budget -> _batch_sizer.estimate_token_budget).

Adds 131 orthogonal tests covering all 4 dialect detection code paths
across input formats, normalization, routing, quoting, encoding,
schema validation, and cross-component consistency.
2026-05-31 10:26:03 +03:00
5deb01e182 fix 2026-05-29 16:15:41 +03:00
525e0a56af fix(translate): add database_id to datasources response + auto-select on frontend
Root cause: fetch_available_datasources did not return database_id,
so frontend could not auto-set target_database_id when user selected
a datasource. User then had to manually pick a database on Target Config
tab — and could accidentally select the wrong one.

Backend changes:
- Add 'database_id' field to datasources response (from db_info.get('id'))
- Add _database_name tracking in SupersetSqlLabExecutor with getter
- Add database_name + database_backend to TargetSchemaValidationResponse
- Enrich resolve_database_id logging with database_name and all_keys

Frontend changes:
- In selectDatasource(), auto-set targetDatabaseId from ds.database_id
  when present, so the correct target DB is used for schema checks.
2026-05-29 15:36:36 +03:00
6743b67605 fix(translate): normalize backend dialect detection with explicit mapping
- Replace fragile substring check (any(kw in backend for kw in ('clickhouse', 'ch')))
  with a proper _backend_normalize mapping + exact comparison.
  Fixes misdetection of Greenplum (postgresql) and other backends.
- Add 'clickhousedb' to CLICKHOUSE_DIALECTS so SQL generator uses
  backtick quoting and correct INSERT strategy for ClickHouse.
- Normalize _extract_dialect to map 'clickhousedb' -> 'clickhouse'
  and 'greenplum' -> 'postgresql'.
2026-05-29 14:32:00 +03:00
d294e0295b fix: QA findings — GRACE contracts for _get_verify, fix _llm_http.py structure
- Added #region/#endregion contracts with @RATIONALE/@REJECTED to
  _get_verify() in both translate plugins (P1 HIGH, P2 MEDIUM)
- Removed orphaned duplicates #endregion in _llm_http.py (P1 HIGH, P6 HIGH)
2026-05-29 11:10:11 +03:00
8ecea09222 fix: capath instead of cafile for all LLM clients
OpenSSL 3.x ignores intermediate CA certs in -CAfile (verify code 20)
but correctly builds chains with -CApath (verify code 0).
All three clients now use capath:
  - service.py: ssl.create_default_context(capath=...)
  - _llm_http.py: verify='/etc/ssl/certs/'
  - preview_llm_client.py: verify='/etc/ssl/certs/'
check_llm_certs.py rewritten for clarity.
2026-05-29 11:02:48 +03:00
19dd447345 feat: ADR-0009 SSL cert management, fix certifi vs system CA
- ADR-0009: comprehensive SSL certificate management strategy
- _get_ssl_verify() returns SSLContext with cafile= instead of bool True
  (httpx deprecates verify=<str>, requests needs system CA, not certifi)
- _get_verify() in translate plugin returns system CA path
- All tests updated for SSLContext return type
- All QA findings C1-C3, H1-H4, M1 incorporated into ADR
2026-05-28 12:06:42 +03:00
60333bdb95 fix: add LLM_SSL_VERIFY support to translate plugin HTTP clients
preview_llm_client.py and _llm_http.py both use requests.post()
without verify=, causing SSLError when corporate CA not in trust store.
Added _get_verify() helper that reads LLM_SSL_VERIFY env var.
2026-05-28 11:53:17 +03:00
ce49760e5a date fx 2026-05-27 16:05:04 +03:00
7ffddff2a6 feat: add LLM SSL verify control and auto CA cert download
- LLM_SSL_VERIFY env var to disable SSL verification for corporate proxies
- install_llm_ca_certs() entrypoint function — downloads PEM/DER certs
  from LLM_CA_CERT_URLS, converts DER→PEM, installs to system CA store
- _format_connection_error() — detailed exception chain logging
- 7 new unit tests for _get_ssl_verify, _format_connection_error, verify= param
- LLM_CA_CERT_URLS and LLM_SSL_VERIFY in .env.enterprise-clean
- Removed duplicate @RELATION DEPENDS_ON -> [EXT:Library:tenacity]
2026-05-27 14:44:46 +03:00
4205618ee6 chore: eliminate all deprecation warnings from tests and linter
Warnings fixed:
- datetime.utcnow() → datetime.now(UTC) across 48+ files (src/ + tests/)
- datetime.utcnow (callback ref) → lambda: datetime.now(UTC) in model fields (18 files)
- Pydantic class Config → model_config = ConfigDict(...) (16 files)
- Pydantic .dict() → .model_dump() (8 files)
- ConfigDict(allow_population_by_field_name=True) → validate_by_name=True
- SQLAlchemy declarative_base() import path updated
- FastAPI on_event → lifespan context manager (app.py)
- Import sorting (ruff I001) auto-fixed across all files
- Fixed broken re-export chains that ruff F401 cleanup broke:
  _validate_bcp47: service.py now imports from dictionary_validation directly
  job_to_response: _job_routes.py and test imports from service_utils directly
  fetch_datasource_metadata: restored re-export in service.py
- Added missing TranslateJobService import in _job_routes.py (was deleted by F401)
- Added ConfigDict(protected_namespaces=()) for DashboardDatasetItem schema field
- pytest.ini: replaced deprecated importmode with asyncio_mode

All 440 tests pass with zero deprecation warnings.
2026-05-26 19:18:28 +03:00
ee08e2f3dd fix 2026-05-26 18:15:37 +03:00
a9a453109c security: critical auth fixes, test migration to SQLite+FK, 44 test fixes
CRITICAL (CVE-class):
- C-4: Remove DEV_MODE fallback with hardcoded postgres:postgres credentials
- C-3: WebSocket endpoints now require JWT or API key token (?token=) auth
- C-1: Superset environment passwords encrypted via Fernet at rest in DB
- H-4: SESSION_SECRET_KEY separated from JWT AUTH_SECRET_KEY
- H-1: Log injection via %s-formatting instead of f-strings
- H-5: X-Trace-ID header validated as UUID4 to prevent trace poisoning

INFRASTRUCTURE:
- New src/core/encryption.py — EncryptionManager extracted from llm_provider
- Test DB: per-module sqlite:///:memory: with PRAGMA foreign_keys=ON
- testcontainers PostgreSQL via TEST_DB=postgres env var
- conftest temp-file global engine (no 10GB shared-cache leak)

TEST FIXES (44 pre-existing → 0):
- test_smoke_plugins: module-level sys.modules mock isolated to per-test fixture
- test_migration_engine: EXT:Python:uuid → uuid syntax fix
- test_dashboards_api: mock env attributes + correct patch target
- test_constants_audit_fixes: sync expected constant names with actual
- test_defensive_guards: patch.object instead of module-level Repo mock
- test_clean_release_cli: removed empty config.json directory
- FK violations: TaskRecord parents in log_persistence, Environment in
  mapping_service, ReleaseCandidate in candidate_manifest_services

ORTHOGONAL TESTS (18 new):
- test_security_orthogonal.py: bcrypt 72-byte limit, unicode, API key
  format invariants, Fernet robustness, encryption key lifecycle,
  log injection protection, JWT edge cases

MODEL FIXES:
- MetricSnapshot.job_id: nullable with SET NULL (was broken FK for
  aggregate prune snapshots, hidden by SQLite)

CLEANUP:
- Removed stale :memory:test_main/test_auth/test_tasks file databases
- Removed duplicate #endregion in encryption_key.py
2026-05-26 14:58:49 +03:00
f49b7d909e feat: GRACE-Poly protocol optimization — ~3200→10 warnings (↓99.7%)
Full optimization cycle:

Protocol (15 files):
- 4-layer SSOT architecture for agent prompts & skills
- Anti-Corruption Protocol consolidated from 5 duplicates
- Tag-to-tier permissiveness matrix (all @tags allowed at all tiers)

Axiom config:
- complexity_rules: all 22+ tags available on C1-C5
- contract_type_overrides: removed (was narrowing per-type)
- 18 new tags added, LAYER enum expanded (Infra, Frontend, Atom, etc.)
- RELATION predicates expanded (USES, CONTAINS, BELONGS_TO, etc.)

Code fixes:
- 2216 @TAG: normalized to @TAG (colon→space)
- 518 [DEF] blocks migrated to #region/#endregion (37 files)
- VERIFIES→BINDS_TO, :Class/:Function suffixes removed, paths→IDs
- 1173-line _external_stubs.py deleted (EXT: handled natively)
- Batch EXT: reference audit (240 targets: 132 external, 99 internal, 9 fix)
- QA regression check: 0 regressions across all checks

Infrastructure:
- DuckDB rebuild stabilized (appender API, INSERT OR IGNORE)
- Anchor regex fix (parent-child BINDS_TO now resolves)
- EXT:*/DTO:/NEED_CONTEXT: regex fixed in validator
- 34MB Doxygen API portal (3194 contract pages)
2026-05-26 11:14:25 +03:00
9ffa8af1dc semantics 2026-05-26 09:30:41 +03:00
31680a1bc9 fix(maintenance): auto-expiry + adaptive markdown height + tests
- Auto-expiry: expired events auto-end on GET /events via task manager
- Height: _estimate_markdown_height adapted to unit=8px scale with padding detection
- CRITICAL-1: removed dead import remove_chart_from_position (QA finding)
- CRITICAL-2: added chart alive check in ensure_banner_chart (QA finding)
- CRITICAL-3: fixed test assertions update_markdown_chart → update_banner_on_dashboard
- @POST contract: fixed return range [2,12] → [19,200]
- Tests: 8 new tests (auto-expiry + layout height)
2026-05-25 08:36:33 +03:00
48cc02ea0b feat: LLM Validation section — 9 API endpoints + 3 Svelte pages + Playwright optimization
Backend:
- /api/validation/ CRUD for validation tasks (policies)
- Run history with 6 filters + pagination
- Alembic migrations (provider_id, policy_id)
- 28 pytest tests

Frontend:
- /validation — task list page with status filters + CRUD
- /validation/[id] — task config (Config + History tabs)
- /validation/history — run history with metrics
- Sidebar nav entry under Operations
- i18n en/ru

Playwright (ScreenshotService):
- Removed 25s hardcoded sleeps → conditional waits
- CDP fallback to full_page screenshot
- Total timeout enforcement (120s)
- Debug screenshots → temp dir with cleanup

QA fixes:
- Debug screenshot accumulation in production (tempdir + rmtree)
- Frontend API payload field name mismatch
- History issues field reference fix
- delete_runs scoped by policy_id

Belief protocol:
- @RATIONALE/@REJECTED on 27 C4+ contracts

Closes: VALIDATION-REWORK-2026
2026-05-21 20:29:36 +03:00
36643024cf refactor(connections): remove ConnectionConfig, migrate mapper to SQL Lab
Backend:
- Remove ConnectionConfig model, CRUD routes (connections.py), and tests
- Remove ensure_connection_configs_table from database.py
- Migrate MapperPlugin from psycopg2 direct PostgreSQL to SupersetSqlLabExecutor
- Add DatasetMapper.get_sqllab_mappings() with default information_schema query
- Update MapColumnsRequest: connection_id → database_id + sql_query
- Fix pydantic_settings v2 deprecation (Field(env=...) → validation_alias)
- Clean up app.py, routes/__init__.py, database.py imports

Frontend:
- Remove DatabaseConnectionsTab, ConnectionForm, ConnectionList components
- Remove /settings/connections route page and Navbar link
- Remove connectionService.js, connections i18n files
- Update MapperTool.svelte: postgres → sqllab source
- Update Map Columns modal: database selector + SQL query instead of connection_id
- Clean up settings page tabs, nav links, test mocks
- Clean up i18n keys (settings, nav, mapper)

Tests: 25/25 pass (14 030-feature + 11 route tests)
Build: succeeds
2026-05-21 18:47:41 +03:00
bd55697e94 fix(semantics): curator audit — Constant→Block, drop :Class suffix on relations 2026-05-21 00:02:45 +03:00
68740cd9ed semantic 2026-05-20 23:54:53 +03:00
084f782065 lang detect 2026-05-20 17:15:31 +03:00
904631efe9 fix(semantics): add function-level GRACE contracts to _lang_detect.py
All 5 functions now have proper #region/#endregion contracts:
- _detector_cache_key [C:1] — private helper
- build_detector [C:2] — public API
- get_detector [C:2] — public API with caching
- detect_language [C:2] — public API, @RAISES for safety
- batch_detect [C:3] — public API with @RELATION edges

Clean audit: 0 warnings on _lang_detect.py
2026-05-20 14:39:06 +03:00
36608ac368 fix(semantics): upgrade complexity tiers, add missing relation edge
- LanguageDetectService: C2→C3 (has @RELATION, requires C3)
- LanguageDetectServiceTests: C2→C3 (has @RELATION, requires C3)
- preview.py: add @RELATION DEPENDS_ON -> [LanguageDetectService]
2026-05-20 14:34:43 +03:00
04a20f7d3c feat(translate): replace LLM-based language detection with local lingua-detector
- Add _lang_detect.py module wrapping lingua-language-detector (pure Python, 0.076ms/call)
- Add batch_detect() with detector caching for performance
- Pre-filter rows where detected source language matches target (same-language skip)
- Remove detected_source_language from LLM prompt templates (token savings)
- Use local detection for cached rows (was always 'und')
- Preserve backward compat: LLM detected_source_language as fallback when lingua returns 'und'
- Add 24 unit tests for detection, edge cases, caching, and mapping
2026-05-20 14:31:37 +03:00
09d12b3b68 semantics 2026-05-20 09:59:03 +03:00
b916ef94d5 semantics 2026-05-19 18:36:15 +03:00
ee33f1d7fb fix(translate): fix job creation tab navigation and preview validation
- frontend: add  to reload job data on param change after goto
- frontend: set existingJob from createJob response for immediate tabs
- frontend: suppress error toasts for schedule 404 (normal state)
- frontend: conditional ScheduleConfig mount to avoid 'new' job loading
- backend: skip preview check for jobs with direct Superset datasource
- backend: add orthogonal test for datasource+no-preview success case
2026-05-18 10:56:21 +03:00
4479392a2f fix(translate): handle row lock timeout in cancel, add flush fallback, migrate progress to store
- Extract _fallback_cancel_request for row lock timeout recovery in orchestrator_cancel
- Add flush lock timeout fallback in cancel_run
- Set error_message when all records fail in executor
- Track insert_failed state in scheduler and frontend store
- Migrate TranslationRunProgress from local polling to centralized store
- Fix nginx resolver for Docker variable-based proxy_pass
- Add FRONTEND_HOST_PORT env var to docker-compose
2026-05-18 10:36:58 +03:00
9228d071ef fix(translate): fix batch sizing — use real available input budget, respect job.batch_size, lazy playwright in docker
- _batch_sizer.py: compute per_batch_budget from actual available_input_budget
  (context_window - max_output_tokens) instead of sum of first N rows
- _batch_sizer.py: cap max_rows_hard_cap with job.batch_size (user config)
- _token_budget.py: add available_input_budget and max_output_tokens to return
- preview.py: validate required config fields before preview
- requirements-docker.txt: add playwright pip package (~5 MB)
- backend.entrypoint.sh: lazy playwright install chromium on first start
- build.sh: switch tar to tar.xz (-T0 -9), 5.5x smaller bundles
- README.md: add offline bundle deployment instructions
- playwright.config.js: add testMatch for *.e2e.js files
- frontend: preview tab config validation, i18n keys, translationRun store
2026-05-17 23:32:00 +03:00
de87739eab chore: cleanup tracked junk + commit remaining test fixes
.gitignore:
- Add .duckdb semantic index binaries
- Add .axiom/temp/ pytest artifacts
- Add e2e_*.png screenshots
- Remove tracked package-lock.json (already in gitignore)

Untrack junk (git rm --cached):
- 3 duckdb binary index files
- 18 pytest temp artifacts
- 12 e2e screenshots
- package-lock.json

Test fixes (decomposition follow-up):
- executor test mock paths
- orchestrator test mock paths
- preview test mock paths
- orthogonal fixes test mock paths
- git_manager integration test
- settings_page integration test
- settings-utils fix
2026-05-17 19:22:09 +03:00
f872e610a9 refactor: decompose oversized contracts to satisfy INV_7 fractal limit
Break monolithic modules >400 lines into focused sub-modules while
preserving backward-compatible imports and all test coverage:

Backend (Python):
- TranslationExecutor: 1974→241 lines, split into 9 sub-modules
- Translate plugin: orchestrator (1137→148), preview (1303→244),
  service (1052→275), dictionary (1007→68)
- ProfileService: 857→172 with 4 extracted sub-modules
- TaskManager: 708→322 with graph/event_bus/lifecycle extracted
- Test dictionary: 1199→split into 6 focused test files

Frontend (Svelte):
- SettingsPage: 1451→291 with 6 extracted tab components
- GitManager: 1220→228 with 5 extracted panels
- DatasetReviewWorkspace: 1202→314
- translate.js API: 664→28 barrel with 6 domain modules

Protocol:
- Remove single-contract 150-line limit from INV_7 (keep CC≤10)
- Fix unclosed #endregion tags across 11 files
- Fix 19 test regressions from stale mock paths
- All 294 tests passing
2026-05-17 19:18:32 +03:00
cd868df261 fix(health): suppress 404 when health monitor disabled + fix audit test assertion
- Sidebar.svelte: defer healthStore.refresh() until feature flags confirm
  health_monitor is enabled; skip entirely when disabled
- health.js: remove throw error from catch block — log and return null
  instead, preventing 'Uncaught (in promise)' on expected 404
- test_report_audit_immutability.py: fix mock assertions —
  audit_service uses logger.reason/reflect/explore, not logger.info
- HealthStore and Sidebar now produce zero network noise when
  FEATURES__HEALTH_MONITOR=false
2026-05-17 14:18:02 +03:00
58ac89c21e fix(translate): token budget overhaul + truncation retry + smart batch sizing + LLM provider fixes
Token Budget (_token_budget.py):
- DEFAULT_MAX_OUTPUT_TOKENS 8192→16384 (adequate for 50 rows×4 langs)
- Add PROVIDER_DEFAULTS with per-model context_window/max_output_tokens
- OUTPUT_PER_ROW_PER_LANG 60→120, add JSON_OVERHEAD_PER_ROW=50
- PROMPT_BASE_TOKENS 300→600, MAX_OUTPUT_HEADROOM 1000→3000
- CJK-aware token estimator (_estimate_tokens_for_text: 1.5 chars/tok for CJK)
- Output-aware batch sizing (_apply_output_aware_batch_sizing)
- Warn on dictionary cap mismatch

Executor (executor.py):
- Return finish_reason from _call_openai_compatible→_call_llm
- Truncation detection + batch splitting on finish_reason=length
- Smart batch sizing (_auto_size_batches): greedily split by row token budget
- Fix disable_reasoning hardcoded 8192→use calculated max_tokens
- TypeError guard on choices[0] access, base_url validation
- Broadened response_format fallback (matches response_format|structured|json_object)
- Remove invalid extra_body, fix partial-recovery regex for integer row_id
- 18 new tests for estimate_row_tokens and _auto_size_batches

Preview (preview.py):
- Align token constants with _token_budget.py (CHARS_PER_TOKEN=2.2, OUTPUT=120)
- Return finish_reason, broadened response_format fallback
- Fix hardcoded 8192, base_url validation

LLM Services:
- render_prompt: detect unfilled {placeholders}, log WARNING
- llm_provider: distinguish crypto exceptions (InvalidTag vs ValueError vs generic)
- Respect Retry-After header on HTTP 429
2026-05-16 09:58:31 +03:00
4f6544ab1a fix(translate): exclude key columns from cache hash — only text + context fields
- _compute_source_hash now accepts context_keys parameter
- source_data is filtered to only include context-relevant fields (not row_id, primary keys)
- If no context_columns configured, hash is based on source_text + dict_hash + config_hash
- This ensures identical text in different rows produces a cache hit
2026-05-16 09:33:15 +03:00
b466ac6211 feat(translate): add translation cache with source_hash dedup + enforce dictionary post-processing
- Add source_hash column (SHA256 of source_text+source_data+dict_hash+config_hash) to TranslationRecord for cache dedup
- Add Alembic migration b1c2d3e4f5a6 for the new column and composite index (source_hash, status)
- Implement _compute_source_hash / _check_translation_cache in executor.py — before LLM call, check if same source+dict+config combo was already translated successfully
- Implement cache-aware routing in _process_batch: full cache hit → skip LLM, partial hit → route to LLM
- Store source_hash on all new TranslationRecord rows for future cache hits
- Strengthen dictionary prompt instructions from 'use when applicable' to 'MUST use — mandatory'
- Add _enforce_dictionary() post-processing: force-replace dictionary terms in LLM output if LLM ignored them
- Fix CRITICAL: use run.dict_snapshot_hash/config_hash (exist on TranslationRun, not TranslationJob)
- Fix MAJOR: verify cached languages cover ALL target_languages before accepting cache hit
- Fix MAJOR: prevent regex back-reference injection in dictionary enforcement (use lambda)
- Fix: add joinedload to cache lookup to avoid N+1 queries
- Fix: remove redundant single-column index (composite index is sufficient)
- Fix(frontend): parse paginated /translate/dictionaries response correctly (result.items)
2026-05-16 00:42:07 +03:00
2ece47d561 fix(translate): log RUN_CANCELLED event on flag cancel, skip source-lang TranslationLanguage entries
- Add RUN_CANCELLED event logging in orchestrator when executor returns CANCELLED (cancel flag path)
- Skip TranslationLanguage creation for languages matching detected source language (no ru in stats)
- Update tests for new behavior (3 entries instead of 4, no fr entry for source-match)
- Fix clickhouse insert test mocks for .options(joinedload()) chain
- All 208 translate tests pass
2026-05-15 22:15:53 +03:00
e754efc7bd feat(llm): add LiteLLM provider — enum, client, tests, QA follow-ups
- Add LITELLM = "litellm" to LLMProviderType enum (already in HEAD from prev commit)
- Add comment in LLMClient.__init__ explaining standard Bearer auth for LiteLLM
- Add regression test test_provider_type_enum_values — guard against value drift
- Add test_litellm_client_uses_default_bearer_auth — verify no extra headers
- Clean up duplicate @RELATION lines in models.py
- All 20 backend + 4 frontend tests pass
2026-05-15 22:11:55 +03:00
27168664b8 fix(translate): normalize Unix timestamps to YYYY-MM-DD for ClickHouse Date columns
- Add _normalize_timestamp_value to key column values in orchestrator.py and executor.py before SQL generation
- Fix test mocks for .options(joinedload()) chain, explicit None attrs for mock job
- Add global translation run progress indicator (TranslationRunGlobalIndicator + store)
- Fix translate page import missing translationRunStore
- All 208 translate tests pass
2026-05-15 22:06:27 +03:00
fdf48491a1 fix(translate): Kilo API response_format, reasoning_effort skip, structured_outputs fallback, refusal handling
- Enable response_format (json_object) for all providers including Kilo/OpenRouter
- Skip reasoning_effort for Kilo/OpenRouter (returns 400 — mandatory reasoning)
- Add structured_outputs fallback: retry once without response_format if upstream
  provider (e.g. StepFun) rejects it with 'structured_outputs is not supported'
- Handle model refusal field (refusal) instead of treating as empty content
- Fix None-handling guards for message/finish_reason/content fields
- Apply same fixes to both preview.py and executor.py _call_openai_compatible
- Connect orphaned TermCorrectionPopup, BulkReplaceModal, BulkCorrectionSidebar
2026-05-15 18:12:29 +03:00
70aec51e0b fix(translate): partial JSON recovery for truncated LLM responses when DeepSeek runs out of tokens
DeepSeek v4 Flash returns finish_reason=length when reasoning uses
all output tokens. Partial row extraction via regex recovers complete
rows from truncated JSON instead of failing with 400 error.

Also: extra_body reasoning_effort for Kilo/OpenRouter proxied providers
2026-05-15 10:31:51 +03:00
ff4804164a feat(translate): universal reasoning suppression for DeepSeek/Qwen/MiniMax/Kimi
Added disable_reasoning toggle that works via:
1. API parameter: reasoning_effort: 'none' (DeepSeek, Qwen)
2. System prompt instruction: 'Respond directly with ONLY the JSON.
   Do NOT include any reasoning, thinking, or chain-of-thought'
   (works for ALL models universally)

Backend: model column, schema, preview+executor payload
Frontend: checkbox in LLM settings, i18n en+ru
2026-05-15 10:13:37 +03:00
4641c82397 feat(translate): auto batch_size estimator for LLM token budget
New _token_budget.py calculates safe batch_size and max_tokens based on source text length, target languages, dictionary size, and model context window (64K DeepSeek v4 Flash). preview.py: auto-reduces sample_size when texts are long. executor.py: per-batch dynamic max_tokens. 12 new tests.
2026-05-15 09:53:54 +03:00
365d710806 fix(translate): increase max_tokens 4096→8192 for DeepSeek multi-language output
finish_reason=length from DeepSeek v4 Flash — model runs out of
output tokens due to reasoning_content (CoT) + multi-language (4 langs).
8192 tokens ensures complete JSON response for preview and execution.
2026-05-15 09:46:37 +03:00
5d4649d24e fix(translate): add detailed LLM response logging for empty content debug
Preview + executor now log finish_reason, message_keys, and raw
response structure when LLM returns empty content, enabling
diagnosis of DeepSeek API issues (empty content with finish_reason=length)
2026-05-15 09:28:21 +03:00
7947700188 fix(translate): add EXPLORE logging for LLM JSON parse failures
Preview + executor now log the raw LLM response (first 1000 chars)
when JSON parsing fails, enabling debugging of malformed LLM output.

Previously ValueErrors from JSON parse failures were silently returned
as 400 without any diagnostic data in logs.
2026-05-15 09:15:36 +03:00
a74c50206a feat(translate): enhanced target table column mapping + fix multi-language save
Multi-language save fix:
- MultiSelect.svelte: selected prop changed to $bindable([])
  (without $bindable, bind:selected never propagated to parent component,
  so target_languages always stayed as default ['en'])

Target table column mapping (INSERT enhancement):
- target_language_column — stores language code (e.g. 'ru', 'en')
- target_source_column — stores original source text
- target_source_language_column — stores detected BCP-47 source language
- SQL generator includes these columns in INSERT when configured
- Frontend: 4 input fields under 'Target Column Mapping' section
  with clear labels, placeholders, and i18n hints

Backend: model, schema, service, orchestrator
Frontend: job config page, i18n en+ru
DB: 3 new columns added to production
2026-05-15 09:09:39 +03:00
77d55c8e69 refactor(translate): remove all deprecated columns from model + DB
Removed 6 legacy columns replaced by Phase 11 multi-language models:
- TranslationJob.target_language → target_languages (JSON)
- TranslationRecord.{llm_translation, user_edit, final_value} → TranslationLanguage
- TerminologyDictionary.{source_language, target_language} → DictionaryEntry per-entry

Affected files: model, schemas, 5 plugins, 4 test files
Alembic migration: aa1b2c3d4e5f (DROP COLUMN IF EXISTS)
Production DB: columns dropped via ALTER TABLE

Tests: 234 passed, 0 failed
2026-05-15 00:06:41 +03:00
9f3f6611a1 feat(translate): complete Phase 10-11 — full spec 028 closure (T075-T134)
Phase 10 closure:
- T075: NotificationService wiring for scheduled-run failures
- T077: Semantic audit via axiom MCP (0 warnings)
- T078: Quickstart validation — all 165+280 tests pass

Phase 11 completion:
- T083-T086: Multi-language models/schemas/Alembic migration
- T087-T090: Auto-detect source language (BCP-47) + tests
- T091-T095: Multilingual dictionaries with language-pair filtering
- T096-T108: Multi-language job config → preview → execution pipeline
- T109-T118: Inline correction + BulkReplaceModal + CorrectionCell + tests
- T119-T122: Per-language history and MetricSnapshot
- T123-T134: Context-aware corrections (Jaccard similarity, priority flagging, context_data)

New files: Alembic migration, test_scheduler, test_inline_correction,
BulkReplaceModal, vitest for CorrectionCell + BulkReplaceModal

Total: 134/134 tasks complete. Backend 165p, Frontend 280p.
2026-05-14 21:14:04 +03:00