Skip to content

Tags: Zipstack/unstract

Tags

v0.184.1

Toggle v0.184.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[MISC] Fail rig groups that inherit an unrelated pytest config; skip …

…order-dependent connector tests (#2205)

* [FIX] Reject rig groups that inherit an unrelated project's pytest config

`cd workdir && pytest` does not pin config to workdir — pytest walks up
until it finds one. A group whose workdir is a nested project without its
own config therefore silently adopts its parent's addopts (coverage
targets, --strict-config) and fails for reasons unrelated to its tests.
The failure is opaque: a usage error, or zero tests collected.

Resolve the config file the same way pytest does and fail the group up
front with a message naming the offending file and the fix. Inheriting
the repo-root config stays allowed — that is the normal monorepo case,
and two groups rely on it today.

Verified against all 18 existing pytest groups: none are rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* [FIX] Skip order-dependent destination-connector and SharePoint tests

These tests share a fixed table (or remote path) across test methods with
no working teardown — several tearDown bodies are a bare `pass` with the
drop statements commented out. Results therefore depend on execution
order, and the SharePoint read test consumes what the write test leaves
behind.

They are inert today only because the credentials they need are not
provisioned, so the breakage would surface as a surprise the moment
someone adds them. Skip explicitly, with the reason stated, so enabling
credentials is a deliberate step rather than an accident.

The postgres variant is deliberately untouched — it runs today and is
being fixed rather than skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Run the workers integration lane against the rig's Postgres

The lane reported 141 tests, 141 skipped: the rig provisions a
testcontainers Postgres and exports DB_*, but the workers fixtures read
TEST_DB_* and so fell back to the dev-compose defaults, and nothing ever
applied the pg_queue schema.

Mirror the provisioned DB_* onto TEST_DB_* for groups requiring postgres
(defaulting DB_SCHEMA to public — a bare container has no other schema),
and apply defaults.postgres_migrate once after the container is up and
before any group runs. Schema is a property of the database rather than
of a group, and guarding on the provisioned URL keeps it a no-op under
the compose runtime, where the platform's own backend migrates on start.

Django migrate rather than DDL in a fixture: the lane asserts against the
real generated CheckConstraint, which hand-written DDL would render
self-referential.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [MISC] Tighten rig DB-wiring comments

Concise, generic WHY-only; drop stale-prone references to specific
function names and test lists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Skip the pre-group migration when no group needs Postgres

The testcontainers runtime provisions the full infra set, so a
connector-only run would still trigger the backend migration and could
be aborted by an unrelated migration failure. Gate on a runnable group
actually declaring a Postgres requirement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Seed last_progress_at in pg_barrier_state test inserts

The column is NOT NULL with only a Python-level default, so raw-SQL
seeds that omit it hit a NotNullViolation once the tests run against a
real migrated database. Add now() to the four barrier seed inserts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Isolate the pg_queue suite per xdist worker via its own schema

This is the only suite that opens a raw psycopg2 connection, so it
bypasses pytest-django's per-worker test database. Under xdist every
worker shared one physical table, and the reaper/sweeper paths scan all
rows — so a whole-table assertion in one worker saw another worker's
rows.

Give each worker its own schema (cloned from the rig-migrated public via
LIKE ... INCLUDING ALL, the tables have no cross-table FKs) and truncate
between tests, since a raw connection carries committed rows forward with
no rollback. Cuts the lane from 35 failures to 0 at serial and -n2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Pin schema-env fixture after the os.environ reset

The rig injects TEST_DB_SCHEMA=public into the group env, which conftest
captures into the pristine baseline that _restore_os_environ restores around
every test. _pg_worker_schema_env sets the per-worker schema (test_gwN), but
with no dependency edge the two same-scope autouse fixtures ordered only by
coincidence — in CI _restore_os_environ ran last and clobbered the schema back
to public, so every xdist worker shared the one public table and the barrier /
reaper whole-table DELETEs wiped each other's in-flight rows (6 integration
failures). Depend on _restore_os_environ explicitly so the schema env is set
after the reset and survives into the test body.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [MISC] Address CodeRabbit review nits

- README: document the actual defaults-level key (postgres_migrate), not migrate
- test_cli: isolate REPO_ROOT via monkeypatch so the walk stays in tmp_path
- test_groups: escape the regex dot in pytest.raises match= (RUF043)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Address PK review on rig config guardrail + PG schema fixtures

- conftest: derive base PG schema from TEST_DB_SCHEMA/DB_SCHEMA (not literal
  public) so a local `pytest -m integration` finds tables migrated into
  `unstract`; clone per-worker schema from that base.
- conftest: gate the per-test schema-env/truncate fixture on the integration
  marker + PG reachability so the DB-free unit lane no longer opens a
  connection (and pays retry-sleep) before every test, nor mutates DB_SCHEMA.
- cli: config-isolation guardrail resolves from the paths' common ancestor
  (what pytest actually does), catching a nested project whose paths reach
  past the workdir; match config sections at line start; run the guardrail in
  `validate` too; broaden the fix hint to pytest.ini when no pyproject exists.
- cli: layer the provisioned connection over postgres_migrate spec.env so a
  stray DB_HOST/DB_NAME can't redirect migrations; TEST_DB_* mirror uses
  setdefault to respect a group's escape hatch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [TEST] Isolate destination-connector test table per xdist worker

Under rig parallelism, sibling test methods in this TestCase all wrote to a
single `output_3` table; a concurrent worker's row could win the
`ORDER BY created_at DESC LIMIT 1` read and fail the data assertion with a
null column. Give each worker its own table so the reads can't collide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Harden per-worker schema clone (parameterized identifiers, fresh drop)

Address CodeRabbit: build the schema/table DDL via psycopg2.sql identifiers
instead of f-strings, and DROP the per-worker schema before recreating so a
reused local DB doesn't keep a stale clone whose table definitions have since
changed (CREATE IF NOT EXISTS would silently no-op).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.184.0

Toggle v0.184.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[FIX] Propagate all *_cls/*_class plugin metadata fields in PluginMan…

…ager (#2201)

* [FIX] Propagate all *_cls/*_class plugin metadata fields, not a hardcoded 3

PluginManager.load_plugins() only copied entrypoint_cls, exception_cls,
and service_class from a plugin's metadata into the registered plugin
dict. The verticals_usage plugin (which feeds API deployment usage into
the API Hub subscription dashboard) also defines headers_cache_class,
which was silently dropped, causing every plugin["headers_cache_class"]
lookup in api_hub_usage_utils.py to raise KeyError - swallowed by broad
except blocks - so API hub headers were never cached and usage never
reached the verticals.subscription_usage table.

* [FIX] Guard non-string metadata keys and add review-flagged test coverage

- key.endswith() on a non-string metadata key raised AttributeError,
  uncaught by the surrounding except KeyError, crashing load_plugins()
  for every plugin process-wide (CodeRabbit).
- Hoist the _cls/_class suffix tuple to a module constant and trim the
  comment so it doesn't reference one enterprise plugin's field name.
- Note that the `key not in plugin_data` guard is purely defensive today.
- Add tests locking in that a *_class field propagates by name even when
  its value is None, and that is_active=False also skips loading.

---------

Co-authored-by: Athul <89829560+athul-rs@users.noreply.github.com>

v0.183.0

Toggle v0.183.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3636 [PERF] Stop the test rig wasting CPU and hashing time (#2195)

* UN-3636 [FEAT] Merge overlay manifests via UNSTRACT_RIG_EXTRA_MANIFESTS

load_groups() now merges extra group manifests listed in the env var
(os.pathsep-separated, REPO_ROOT-relative) onto the base tests/groups.yaml
before validation, so cross-manifest depends_on and the platform-gate
invariant are checked over the union. Name collisions are an error.

Lets a downstream repo (the cloud build) contribute its own test groups by
copying a groups.cloud.yaml into the merged tree, without editing the OSS
manifest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Skip org lookup when no org in context

UserContext.get_organization() ran Organization.objects.get() even with no
org id in StateStore (import time, or management commands with no request),
catching only DoesNotExist/ProgrammingError — a DB-less/unmigrated setup hit
an uncaught OperationalError. Short-circuit when there's no org id: no query,
so serializers/managers that reference org-scoped querysets at class-def can
be imported during DB-free test collection.

* UN-3636 [FIX] Scope overlay manifests to the default manifest

Address review feedback on the UNSTRACT_RIG_EXTRA_MANIFESTS overlay:

- Overlays now apply only when loading the default manifest, so an
  explicit `load_groups(path)` (test fixture, ad-hoc manifest) can no
  longer absorb a downstream repo's ambient overlay.
- `_merge_manifest` returns the merged defaults so an overlay can rename
  `platform_gate_group` instead of having it silently ignored.
- A bad path in the env var raises a ValueError naming the variable
  rather than a bare FileNotFoundError/IsADirectoryError.
- Extract `_load_manifest_dict` to single-source manifest parsing and its
  error message.
- Tests: drive the real default-manifest path; cover overlay isolation,
  overlay defaults, malformed overlay, and a missing overlay path.
- Pin the truthy branch of UserContext.get_organization so inverting the
  guard can't pass unnoticed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Stop re-running cross-tier deps in every tier leg

`--tier X` expanded each selected group's `depends_on` transitively, with
no tier bound. `integration-workflow-execution` and `e2e-smoke` both
declare `depends_on: [unit-sdk1, unit-workers]`, so those two unit groups
ran again in the integration leg and a third time in the e2e leg.

Tiers run as separate CI legs and the unit leg already covers them, so
dep expansion is now bounded to the requested tier. Explicitly named
groups are never dropped, and intra-tier deps (e2e-smoke -> e2e-login)
still expand and order as before. Unrun deps do not weaken gating:
`blocked_by` intersects with groups that failed in the same run.

Measured on the last main run: ~88s of unit-workers and ~48s of
unit-sdk1 re-executed per run. On the cloud CI runner the same
duplication costs ~350s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [MISC] Drop the ENVIRONMENT gate on the LLM mock (#2191)

* UN-3636 [FIX] Drop the ENVIRONMENT gate on the LLM mock

It did not defend the case it was added for. The threat was a worker env block
copied out of the test overlay into a real deployment, but the gate was written
into that same block, so a copy carries it. Base compose also sets
ENVIRONMENT=development on both workers that run the injection, so any
deployment derived from it satisfied the gate regardless. That left one real
case -- the mock var set alone somewhere that sets no ENVIRONMENT at all --
which holds by accident rather than design, in exchange for depending on a
variable nothing else in the codebase reads.

What actually guards the hatch is unchanged: it is off unless someone sets
UNSTRACT_LLM_MOCK_RESPONSE, and it warns once per process while active. Making
mocked spend distinguishable downstream is the defence worth having, and it
belongs on the usage record rather than in a config check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3636 [MISC] Drop the unread ENVIRONMENT variable from compose

Nothing reads it: no service, worker, frontend or plugin looks the variable
up, and the one consumer it ever had — the LLM mock gate — was removed in the
previous commit. Dropping it everywhere keeps a dead knob from looking
load-bearing to the next reader.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ryg9chVDJQggCybpq3YoY3

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3636 [MISC] Tighten code comments

Drop lines that restate the code, trim session-specific detail, and merge
comments that duplicated each other across a module and its test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Gate overlay merging on an omitted path, not its value

`load_groups(DEFAULT_MANIFEST)` merged overlays even though the caller named a
manifest explicitly, because the check compared path values. Path equality is
also spelling-sensitive, so the same file relative and absolute behaved
differently. Key on `path is None` instead: an explicit path loads exactly what
it names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ryg9chVDJQggCybpq3YoY3

* UN-3636 [FIX] Stop ide_prompt_complete tests from hitting the network

TestIdePromptComplete drives the full success path, which reaches
client_plugin_registry.get_client_plugin("subscription_usage"). In OSS no
such plugin is installed, so the lookup returns None instantly. In a tree
with the cloud plugins copied in, it resolves to a real plugin that POSTs
to the backend; with no backend running the call only fails after a
multi-second connect timeout, and _track_subscription_usage swallows the
error so the tests still pass. That accounted for ~190s of the cloud
unit-workers run.

The file already declared _PATCH_GET_PLUGIN but never applied it outside
the dedicated subscription-usage classes. Apply it as a class-scoped
fixture so the lookup is pinned to the OSS answer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [PERF] Stop the test rig wasting CPU and hashing time

Three independent wins measured on the cloud-merged tree:

- `-n auto` collapsed to a single worker on any group shipping psutil,
  because xdist prefers physical cores there. Resolve the count in the
  rig instead, capped at 8 to avoid contending on the test database.
- `--no-migrations` builds the schema from the models rather than
  replaying the full migration history once per xdist worker.
- Test fixtures were paying 600k-iteration PBKDF2 per seeded user.

integration-backend fell from 163s to ~52s with an identical result set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [TEST] Cut rig CI overhead and unblock unit-workers-cloud

Second performance pass on the test rig:

- unit-workers-cloud ran zero tests: its group `PYTHONPATH` overwrote the
  rig-injected plugin dir, so `-p rig_critical_path` failed to import. Merge
  the two instead of letting env.update clobber it.
- Drop `-s` from backend addopts — it disabled capture and flooded the log.
- Persist uv's cache across runs via setup-uv enable-cache, so per-group
  `uv sync` links from cache instead of refetching.
- No-op the real backoff sleeps in the sdk retry tests (~7s -> ~1s); no test
  asserts on elapsed time.
- TestCleanupTasks needs no transaction semantics; TestCase over
  TransactionTestCase drops the per-test truncate-and-reseed.
- Reject a non-mapping `groups:` manifest instead of crashing later.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [MISC] Drop stale-prone CI comments

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.182.0

Toggle v0.182.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[MISC] Update OpenRouter adapter logo to refreshed brand mark and add…

…ed padding to Mistral logo (#2186)

* [FIX] Update OpenRouter adapter logo to refreshed brand mark

OpenRouter refreshed its brand; the old grey routing-arrows mark is
retired. Replaces the adapter icon with the new glyph (rendered from
the official brand SVG), keeping the same 512x512 transparent PNG.

Ref: https://openrouter.ai/blog/announcements/brand-refresh/

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L549habQ6K2m1ACJ7DwtsE

* [FIX] Re-pad Mistral adapter logo for consistent margins

The Mistral pixel-M icon ran edge-to-edge with no horizontal padding,
inconsistent with the other adapter icons. Re-center the mark on the
512x512 transparent canvas with even margins.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L549habQ6K2m1ACJ7DwtsE

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.181.0

Toggle v0.181.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3741 [FIX] Show created/modified metadata on adapter and Prompt St…

…udio list pages (#2182)

* UN-3741 [FEAT] Show created/modified metadata on adapter and Prompt Studio list pages

Adds an 'Updated <relative time>' hint plus an info icon with hover
tooltip (created at, last modified, model / prompt count when present)
to the shared ListView widget used by the adapter and Prompt Studio
list pages.

Backend: expose created_at/modified_at on AdapterListSerializer, and
surface the latest prompt modified_at on the Prompt Studio list —
prompt edits never touch the CustomTool row, so its own modified_at
understates activity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [MISC] Vertically center list-row metadata and even out spacing

Restructure ListView rows into three vertically-centered columns
(title+description, owner+updated, actions) so the metadata cluster
centers against the whole entry instead of hugging the title line.
Consistent flex gaps replace the old width-percentage layout; row
padding bumped for breathing room.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [MISC] Cap list description width for uniform rows

Full text remains available via the ellipsis tooltip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [MISC] Address review feedback: pin test timestamp, guard deprecated actions

- Pin the prompt's modified_at to a known future instant in the list
  test so back-to-back creates can't tie on coarse clocks (Greptile P2)
- Block edit/share click handlers on deprecated items to match their
  disabled styling (CodeRabbit)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [FIX] Address review: bump tool modified_at at source, opt-in Updated label

Backend — maintain modified_at at the source instead of deriving it
(review: derived max could travel backwards on prompt delete, and gave
the same field two meanings across list/detail while breaking
?ordering=modified_at):
- ToolStudioPrompt.save()/delete() now bump the parent CustomTool row;
  the list subquery annotation and SerializerMethodField are gone, so
  the plain field is honest, identical on every endpoint, and orderable
- Tests pin create/edit/delete bumps, cross-tool isolation, and the
  promptless-project case; adapter list endpoint gains coverage
  (timestamps present, rename bumps modified_at, created_at stable)

Frontend — the Updated label is now explicit opt-in (showModified),
passed only by the adapter and Prompt Studio lists; Workflows and
Connectors no longer inherit a misleading value by field presence:
- New timeAgo helper (strict ISO parse, clamped to now) replaces raw
  moment usage: malformed input hides the label instead of rendering
  "Invalid date"
- Meta column no longer swallows row clicks; titles ellipsize like
  descriptions; tooltip fields use one presence convention (!= null)
  and build lazily; centered prop wired into the row Flex (dead CSS
  removed); updateList merges PATCH responses so list-only fields
  survive edits; new CSS classes renamed to the widget's list-view-*
  prefix; two misleading CSS comments corrected

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [FIX] Bump tool on prompt-clearing sync — QuerySet.delete bypasses model delete

sync_prompts clears existing prompts with a queryset delete, which never
calls ToolStudioPrompt.delete() — so a prompts-clearing sync with no
tool-settings payload left the parent's modified_at stale, violating the
invariant the parent-bump establishes (Greptile). The sync now bumps the
tool explicitly when rows were deleted; pinned by a test that runs a
clearing sync and asserts the bump.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [FIX] Harden parent bump: org-scope bypass, honest deleted count, scoped claim

- _touch_tool and the sync bump now use CustomTool._base_manager: the
  org-scoped default manager reads a thread-local and would silently
  match zero rows outside request context; delete() collapsed onto
  _touch_tool (the FK attname survives super().delete()) so the two
  paths can't drift
- sync_prompts reports the per-model prompt count, not the cascade
  total that dragged PromptStudioOutputManager rows into
  "prompts_deleted"; the sync test fixture now carries an output row so
  the assertion discriminates
- List-queryset comment scopes the invariant honestly: only prompt
  writes bump; ProfileManager/DocumentManager and queryset-level prompt
  writes are tracked as UN-3759

Review: PR #2182 round 3 (Jaseem)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-3741 [MISC] Make code comments concise and tracker-independent

Ticket references belong in commits and PRs, not code — they mean
nothing to OSS contributors. Comments and test docstrings now state the
behavior generically; the ProfileManager/DocumentManager bump gap stays
documented in the list-queryset comment without the tracker pointer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

v0.180.0

Toggle v0.180.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3636 [MISC] enable the unit-workers rig group and de-flake its sui…

…te (#2176)

* UN-3636 [FIX] enable the unit-workers rig group and de-flake its suite

The unit-workers group filtered `markers: "unit"`, but no worker test carries
that marker, so it collected zero tests and silently never ran in CI. Switch to
the negative filter the other unit groups use so the 734 worker tests actually
run.

Enabling collection surfaced pre-existing debt (the suite was never CI-gated):

- Test isolation: worker Celery apps build a live-Postgres result backend from
  ambient DB_*/CELERY_BACKEND_DB_*, and building an app hijacks `current_app`.
  Both leaked across tests under xdist (psycopg2 connection errors, task
  `NotRegistered`). Fixed in conftest: strip the DB env before any app import,
  and pin celery's finalized default app as current around each test.
- Stale assertions (prod drifted, tests never caught it): ExecutorToolShim /
  _run_agentic_extraction gained args; index result now carries usage_records;
  the highlight-disabled test over-asserted "plugin loader untouched" when
  lookup-enrichment is queried unconditionally (returns None in OSS).
- Dropped tautological enum-existence tests that just mirror the source enum.
- Renamed the cryptic test_sanity_phase6* / test_phaseN* files to
  intent-revealing names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EN8hh518CbCVMP3BHTVmG

* UN-3636 [DEV] tighten test-rig group comments to concise, generic WHY

Drop stale-prone specifics (function names, file:line refs, vendor
lists, class names) from groups.yaml comments; keep the WHY.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EN8hh518CbCVMP3BHTVmG

* UN-3636 [DEV] address review: real log_component tests, tighten assertions

- Replace the log_component replica (copied tasks.py if/elif, drifted so the
  ide_index case asserted the opposite of production) with tests that drive the
  real execute_extraction, covering the two special-cased branches.
- Match the highlight-plugin assertion on positional or kwarg calls so it can't
  pass vacuously if the call style changes.
- Reword the unit-workers marker note: integration/slow worker tests are
  currently unrouted, not run "elsewhere".
- Cross-reference the two workers conftests on the DB_* env they set vs strip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EN8hh518CbCVMP3BHTVmG

* UN-3636 [DEV] fail the build on a non-optional group that collects nothing

A zero-collect group returns pytest exit 5, which the rig treats as
non-failing — so a broken marker or moved path silently reported green
(the very way unit-workers stayed dormant). Gate on it: a non-optional
pytest group that collects zero tests now fails the overall exit.

Legit zero-collects are exempt: optional groups, hurl (its exit 5 means
"no files"), and dev runs with a --marker/--paths override.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EN8hh518CbCVMP3BHTVmG

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Athul <89829560+athul-rs@users.noreply.github.com>

v0.179.1

Toggle v0.179.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-2742 [FIX] Show profile name instead of LLM name in Output Analyze…

…r tabs (#2030)

* UN-2742 Show profile name instead of LLM name in output tabs

Profiles encode user intent (same model can differ by LLMWhisperer
mode, chunking, etc.), so the profile name is the meaningful label
when comparing outputs. The LLM model name stays available as a
tooltip on each tab.

Applies to the shared tab strip used by the Output Analyzer and the
main combined-output view (JsonView), and the same pattern in
OutputForDocModal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-2742: guarantee non-empty profile tab label and tooltip (review fix)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

v0.179.0

Toggle v0.179.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-2190 [MISC] Auto-capture execution ID in the API deployment Postma…

…n collection (#2031)

* UN-2190 Auto-capture execution_id in API deployment Postman collection

Add a post-response script to the 'Process document' request that
stores message.execution_id into a collection variable, and point the
'Execution status' request's execution_id query param at that variable.
Users no longer copy-paste execution IDs between requests (mirrors the
LLMWhisperer collection's whisper_hash pattern).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-2190 Address review: guard non-JSON responses, scope variable to API deployments

- Wrap pm.response.json() in try/catch so error pages (non-JSON) don't
  surface a Postman test error
- Move collection variables behind APIBase.get_collection_variables()
  so Pipeline collections (no status request) stay variable-free

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* UN-2190 Expose execution_id in the API execution response

The ExecutionResponse DTO carries execution_id but
APIExecutionResponseSerializer dropped it, so the Postman capture
script (and any API consumer) had to parse it out of status_api.
Add it as a first-class response field; the collection script's
message.execution_id lookup now matches the real payload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Address review comments on Postman collection scripts

- Capture script: add else branch that resets execution_id to the
  default sentinel and warns, so a stale id from a previous run is
  never silently reused when the execute response lacks the field.
- PostmanItem.event: use field(default_factory=list) instead of the
  None/[] tri-state; to_dict() now strips empty (falsy) event blocks,
  preserving existing external behavior (pipeline items omit "event").
- Constrain closed enums: EventItem.listen -> Literal["prerequest",
  "test"], ScriptItem.type -> Literal["text/javascript"].
- Document the capture script's response-shape coupling and that it is
  Postman's "test" hook.
- Drop unnecessary string forward-reference on get_collection_variables.
- constants: derive STATUS_EXEC_ID_VARIABLE from EXEC_ID_VARIABLE_NAME
  so the two can't drift.
- Add tests for the postman_collection package covering event
  strip/keep, pipeline vs api-deployment shape, shared-constant coupling
  across variable/URL/JS, and the unencoded {{execution_id}} status URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

v0.178.1

Toggle v0.178.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[MISC] Preserve view function identity in platform-service auth middl…

…eware (#2160)

fix: preserve view function identity in platform-service auth middleware

`authentication_middleware` returned an unwrapped closure, so every
decorated view function's `__name__` was "wrapper". Flask derives an
endpoint name from `__name__`, which meant:

- Six routes carried a redundant `endpoint="..."` kwarg purely to dodge
  the resulting name collision.
- `/usage`, the one authenticated route without that kwarg, registered as
  endpoint `platform.wrapper`.
- Adding any new authenticated route without `endpoint=` would fail at
  import with "View function mapping is overwriting an existing endpoint
  function: platform.wrapper".

Add `functools.wraps` and drop the now-redundant `endpoint=` kwargs. URL
rules and methods are unchanged; only the internal Flask endpoint names
change (`platform.wrapper` -> `platform.usage`). Nothing in the repo
resolves routes via `url_for`, and callers reach platform-service over
HTTP by path, so this is not externally observable.

x2text-service's equivalent middleware already patched `wrapper.__name__`
by hand; this brings platform-service in line.

Also repair `tests/test_auth_middleware.py`, which imported
`unstract.platform_service.main` (removed) and `get_account_from_bearer_token`
(removed) and so broke collection of the entire platform-service suite. It
now covers `validate_bearer_token` against a mocked cursor.


Claude-Session: https://claude.ai/code/session_014gUmgQQ7xV73qn6kcB7mXG

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.178.0

Toggle v0.178.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
UN-3699 [FIX] Fix runner DockerException with requests>=2.32 by bumpi…

…ng docker-py to 7.1.0 (#2155)

UN-3699 [FIX] Bump runner docker-py to 7.1.0 for requests>=2.32 compat

docker-py 6.1.3 is incompatible with requests>=2.32: its UnixHTTPAdapter
only overrides the old get_connection path, so requests 2.32+ (locked at
2.33.0 via unstract-core) routes the internal http+docker:// socket URL to
urllib3, raising URLSchemeUnknown -> DockerException at
DockerClient.from_env(). This breaks any runner image rebuilt from main on
the Docker container-client path (cloud/k8s is unaffected — it never calls
from_env).

Bump docker to 7.1.0, which handles requests>=2.32. Leaves the org-wide
requests==2.33.0 untouched (lowering it is unsatisfiable: workers and
x2text-service require >=2.33.0). Runner uses only stable high-level docker
APIs; websocket-client drops out as it is now an optional docker extra and
the runner uses no websocket features.

Verified: docker 7.1.0 + requests 2.33.0 -> DockerClient.from_env().ping()
== True against a real socket.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>