Commit f742e32
UN-2946 [FEAT] Prompt Studio lookups bridge, executor hook, and IDE wiring (OSS side) (#1929)
* UN-2946 [FEAT] Add lightweight list serializer for Prompt Studio and prompt list endpoint
- Add CustomToolListSerializer for the list action to avoid N+1 queries
(profile lookups, prompt fetching, coverage calculation per tool)
- Add ToolStudioPromptListSerializer with only prompt_id, prompt_key,
enforce_type, sequence_number
- Add GET /prompt-studio/prompt/?tool_id={uuid} list endpoint
- List action uses select_related and Subquery annotation for prompt_count
- Detail endpoint unchanged (still uses full CustomToolSerializer)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Add Look-Ups plugin integration in sidebar nav and routes
- Add lookup-studio plugin detection with dynamic import
- Add PromptStudioPopoverContent for hover submenu (Projects / Look-Ups)
following the same Popover pattern as HITL and Platform Settings
- Register lookups/* route in useMainAppRoutes.js
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FIX] Use .get() fallback for prompt_studio_tool in create_profile_manager
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Add Lookups V2 OSS integration hooks for post-extraction enrichment
- Add lookup_config export in prompt_studio_helper and registry_helper
via cloud plugin guard (try/except ImportError)
- Store raw output in PromptStudioOutputManager, enriched in cloud
LookupOutputResult — preserving both for UI tab display
- Add LookupEnrichmentProtocol and plugin call in post-extraction
pipeline using ExecutorPluginLoader (no-op in OSS)
- Track lookup LLM usage via standard metrics pipeline
(usage_kwargs with run_id/execution_id, capture_metrics)
- Move webhook postprocessing from answer_prompt to pipeline
- Frontend: dynamic plugin imports for LookupMenuItem, LookupIndicator,
LookupOutputTabs in prompt cards; fetch lookup outputs on page load
- Add scroll-to-prompt support via query param in DocumentParser
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 Removed unnecessary gitignore
* UN-2946 [REFACTOR] Deduplicate lookup config helper and add lookup usage reason
- Extract get_lookup_config() to prompt_studio/lookup_utils.py, replacing
4 identical try/except ImportError blocks across prompt_studio_helper
and prompt_studio_registry_helper
- Add LOOKUP to LLMUsageReason choices (was missing, causing invalid
choice on usage records from lookup enrichment LLM calls)
- Migration: usage_v2/0004_add_lookup_usage_reason
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Add get_last_usage() to SDK1 LLM for token tracking
Store prompt/completion/total token counts from the most recent
complete() call on the LLM object itself, making usage data
queryable without relying on the Audit pipeline roundtrip.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [REFACTOR] Split post-extraction pipeline into lookup and webhook methods
Move lookup result-application logic to the cloud plugin, matching
the challenge plugin pattern where the plugin owns metadata mutation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Generic async extraction callbacks and WebSocket transport fallback
Add reusable extraction_complete/extraction_error callback tasks to the
ide_callback worker, replacing the need for Django-based celery workers
for text extraction. Add ExtractionAPIClient for internal API calls.
Add polling fallback to WebSocket transport for local dev reliability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Reduce success notification duration from 2s to 1s for less intrusive UX
* Revert "Reduce success notification duration from 2s to 1s for less intrusive UX"
This reverts commit d6e136d.
* UN-2946 [REFACTOR] Pluggable lookup export validation
Replace inline DRAFT lookup check with pluggable cloud-only hook.
Uses try/except ImportError pattern — zero lookup code in OSS.
Collects all DRAFT lookups in one pass with markdown-linked error messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [REFACTOR] Lookups V2 review cleanup
- Consolidate cloud imports into lookup_utils.py with persist_lookup_output() and validate_lookups_for_export() wrappers
- Fix LookupEnrichmentProtocol.run() return type to None matching challenge/evaluation pattern
- Revert logger.info to logger.debug in websocket_views.py
- Eliminate duplicated LookupOutputTabs ternary with renderWithLookupWrapper helper
- Move lookups menu constants from SideNavBar.jsx to cloud plugin
- Harden DocumentParser.jsx scrollTo with UUID validation and fix useEffect dependency
- Revert SocketContext transport to ["websocket"]
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [UI] Replace sidebar popover with in-page tabs for Lookups
Move Prompt Studio / Look-Ups navigation from a hover popover on the
sidebar into a Segmented control within the ToolNavBar. CustomTools
dynamically imports LookupList from the plugin and renders tabs when
available, falling back to projects-only view in OSS mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Deferred batch usage tracking with operation metrics
Switch from eager per-call Audit HTTP push to a deferred batch write
pattern for adapter usage. LLM/embedding calls stash records in-memory;
the executor flushes them into ExecutionResult metadata; the Celery task
batch-writes via a new internal endpoint.
Adds 5 nullable columns to Usage (reference_id, reference_type,
execution_time_ms, status, error_message) and a composite index for
lookup dashboard queries. Extensible choice lists allow cloud plugins
to register additional usage reasons and reference types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Add plugin hook for lookup output enrichment in serializer
Bridge function in lookup_utils.py lets cloud plugins enrich
PromptStudioOutputSerializer with lookup data (enriched output,
lookup name). Enables real-time lookup results via WebSocket
without page refresh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Add lookup usage observability with error handling and metadata passthrough
- Wire usage_kwargs_extra from lookup config into LLM usage_kwargs for execution observability
- Add error handling around enricher.run() with explicit ERROR usage records
- Generic passthrough of _usage_kwargs into usage records for arbitrary metadata (e.g. reference_id)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Support enriched output copy and lookup drawer plugin hooks
Add dynamic import of getEnrichedCopyText so the copy button copies
enriched lookup output when the Enriched tab is active. Applied to
both single-pass and multi-profile output paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Lookup export validation gate, raw-latest helper, and modified_at fix
- Add /prompt-studio/<pk>/lookup-validation/ endpoint backing the FE
Export/Deploy gate; multi-var block check accepts prompt_ids so a single
prompt run isn't blocked by an unrelated multi-var lookup.
- Add /prompt-output/latest-by-keys/ endpoint that returns the most recent
raw output per prompt_key for the test panel's "Use Latest Outputs"
helper.
- Fix prompt output modified_at not refreshing on re-runs (QuerySet.update
bypasses auto_now); set timezone.now() explicitly in the update args.
- lookup_utils: bridge get_lookup_validation_for_tool and
get_multi_var_lookups_for_tool with prompt_ids scoping.
- Header wires useLookupExportGate via try-import (no-op stub in OSS).
- TokenUsage treats all-null Usage rows as empty.
- CombinedOutput / JsonView build enriched dict from
metadata.lookup_outputs to back the Raw|Enriched output toggle.
- .gitignore: widen docker/compose.*.yaml.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [UI] Fix combined output pill overlap and preserve Look-Ups tab on back
- ProfileInfoBar: swap Row/Col for plain flex-wrap div — kills Ant Row
negative-margin quirk that overlapped wrapped pills in combined output.
- CustomTools: honor location.state.activeTab so back navigation from
lookup detail lands on the Look-Ups tab instead of defaulting to Projects.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Add last_exported_at and wire lookup staleness bridge
Introduces nullable last_exported_at on CustomTool (populated on first
successful export) so staleness checks can compare against downstream
mutations without a data backfill. NULL is treated as "unknown" and
suppresses the lookup-dirty flag to avoid false alarms on pre-feature
projects. Adds the get_latest_lookup_mutation_for_tool bridge in
lookup_utils so OSS stays decoupled from the cloud plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Stream lookup enrichment failures to workflow logs
When enricher.run() raises, surface a user-visible ERROR log line in
the workflow execution log alongside the existing usage record. Keeps
lookup failures observable next to the other pre/post lookup lines we
already emit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [UI] Wire lookup dirty-seed and export gate into ToolIde
Loads useLookupDirtySeed (server-side is_lookup_dirty) and
useLookupExportGate from the cloud plugin via dynamic imports so the
reminder banner reflects lookup changes across page reloads and the
banner's Export flow goes through the same validation modal as the
main buttons. Also adds a titleAdornment slot on ToolNavBar for
rendering the onboarding tooltip and relaxes EmptyState.text to accept
nodes for the tagline + link composition.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [FEAT] Share lookup test wrapper + generic ExecutionLogs back state
- Delegate production lookup enrichment to LookupEnrichment.run_with_metrics
so the executor and the IDE test path share LLM construction, error
handling, and usage-record emission.
- Let ExecutionLogs callers pass an arbitrary backRouteState via location
state so nested UI restore (e.g. a sub-tab) no longer needs special
casing in this component.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [REFACTOR] Round-2 review fixes — OSS side
Bucket of hardening fixes driven by a staff-level PR re-review:
- Org-scope latest_outputs_by_keys (was cross-tenant readable via raw
.objects.filter() that bypassed OrganizationFilterBackend).
- Hide lookup payload shape from OSS: three new opaque bridge helpers
(get_original_value_if_enriched, attach_combined_output_enrichment,
extract_prompt_output_enrichment) replace direct reads of
metadata["lookup_outputs"] / _lookup_outputs / lookup_outputs in
output_manager_helper, CombinedOutput.jsx, and usePromptOutput.js.
- Split usage_v2 index into a new 0005 migration that uses
AddIndexConcurrently + atomic=False so prod doesn't lock the billing
table during build.
- Delete stale workers/tests/test_usage.py that imported the removed
UsageHelper module.
- SDK1 LLM gains public get_last_usage_record() so downstream code
stops reaching into _pending_usage across plugin boundaries.
- legacy_executor stamps metadata["lookup_errors"][prompt] on a failed
lookup outcome for dashboards that surface partial-failure runs.
- extraction_client docstring notes the cloud-only endpoint contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [REFACTOR] Round-3 review fixes — OSS side
- Module-level probe in prompt_studio/lookup_utils.py — swap per-function
try/except ImportError for a single LOOKUPS_AVAILABLE flag. Add
attach_lookup_config / attach_lookup_configs_to_tool_settings helpers
so the direct metadata["lookup_errors"] write and the
lookup_config key stamping both route through the bridge.
- Reject org=None in UsageBatchCreateView (usage_v2/internal_views.py).
- Lift useLookupExportGate to a single mount in ToolIde.jsx; thread
checkLookups down into custom-tools/header/Header.jsx (eliminates
the double modal-portal risk).
- Delete the direct metadata["lookup_errors"] write from
workers/executor/executors/legacy_executor.py — flat summary is now
stamped by LookupEnrichment.write_lookup_error in the cloud plugin.
Replace hardcoded "lookup_llm" metrics key with lookup_cls.METRICS_KEY.
- Trim boilerplate comments across CombinedOutput, PromptCardItems,
PromptOutput, usePromptOutput, prompt-card/Header, CustomToolsHelper,
SideNavBar — keep the why-comments, drop the what-comments.
* UN-2946 [FEAT] Reference prompts by UUID + missing-file gate — OSS side
OSS counterpart to the cloud-side data-model change. Wires the prompt
studio runtime to the new wire shape, surfaces lookup runnability state
in the prompt card, and adds the usage_v2 enum entries the cloud side
records against (lookup as an LLM usage reason, lookup_version as a
reference type).
Partially working — known follow-up:
TODO: rework lookup input UX. The current variable-mapping flow is awkward
(separate rows, manual prompt selection per variable); needs a redesign that
mirrors how users actually compose a lookup template.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [FIX] Surface skipped lookups when source prompt has no value
When a configured lookup runs but extraction returned None for the source
prompt, _run_lookup_enrichment used to fall through silently — leaving
users wondering why enrichment didn't appear. Stream a one-line workflow
log via shim.stream_log so the skip is visible alongside other tool-run
events.
* UN-2946 [REFACTOR] Address Sonar findings on lookups V2 PR
- Extract _init_llm_and_retrieval and _flush_per_prompt_metrics from
LegacyExecutor._execute_single_prompt to drop cognitive complexity
below the gate
- Extract per-prompt helpers from OutputManagerHelper.fetch_default_output_response
- Extract buildDefaultProfileOutputs / buildSelectedProfileOutputs from
CombinedOutput.fetchCombinedOutput
- Give OSS lookup-plugin stub fns matching parameter lists so static
analysis stops flagging call sites as arity mismatches
- Define _UNKNOWN_EXECUTOR_ERROR constant in ide_callback.tasks for the
thrice-duplicated literal
- Use splice(-1, 0, ...) idiom for LookupMenuItem insertion in prompt
card Header
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [FIX] Preserve usage records on executor failure paths
- LegacyExecutor.execute attaches collected usage_records to failure
metadata in the LegacyExecutorError branch
- ExecutionOrchestrator broad-except pulls usage_records off the executor
before wrapping the unhandled exception, so Celery autoretry doesn't
drop billing rows from a partially-completed run
- lookup_utils.get_lookup_validation_for_tool OSS stub returns
incomplete_lookups: [] to match the cloud schema
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [REFACTOR] Squash usage_v2 migrations 3 → 2 for lookups V2
Folds 0006 (choices-only AlterField, no SQL) into 0004 by baking the
final llm_usage_reason and reference_type choice lists directly. Keeps
0005 separate to preserve CREATE INDEX CONCURRENTLY safety on the
billing-critical usage table. Existing envs that already ran 0006 need
a one-time DELETE on its django_migrations row.
* UN-2946 [REFACTOR] Update OSS↔cloud lookup bridge for app rename
Cloud's lookup_v1 plugin is being renamed to lookups; update the OSS
try-import paths (prompt_studio/lookup_utils.py, usage_v2/models.py)
and the related comment in workers/shared/clients/extraction_client.py.
Bridge contract is unchanged — the cloud module just lives at a new
import path.
* UN-2946 [FIX] Address greptile review on lookups V2 OSS
- usage_v2/internal_views.py: wrap bulk_create in try/except so a flush
failure logs ERROR with full context instead of returning a silent 500;
chunk with batch_size=500 to bound transaction size on the
billing-critical usage table.
- legacy_executor: extend the lookup empty-value guard to also skip on
empty strings/lists/dicts so a "" extraction doesn't reach the LLM as
an empty input. Boolean/number 0/False remain valid.
* UN-2946 [FIX] Static usage choices to fix migration drift in OSS CI
Greptile P1: migration 0004_usage_metrics_fields hard-codes the cloud
values "lookup" / "lookup_version" into the AlterField operations,
but the model resolved its choices via a try-import that only fired
when pluggable_apps.lookups was present. In OSS-only builds the
import raised, the model carried 3 choices, and ``makemigrations
--check`` saw drift against the migration's 4 choices — turning OSS
CI red on every run.
Carry the union of OSS + cloud values statically in usage_v2/models.py
and drop the try-import. Cloud-only entries are unused on OSS but
make migration state and model state agree in both builds. The DB is
unaffected (choices is a Django-validator concern, not DDL).
The now-unused CLOUD_LLM_USAGE_REASON_CHOICES /
CLOUD_REFERENCE_TYPE_CHOICES exports are removed from the cloud
plugin's constants module in a separate commit.
* UN-2946 [FEAT] Block enforce_type switch via lookup plugin gate
When a prompt has a lookup configured, switching its enforce_type to a
complex type (table / line-item / agentic_table) is blocked: the cloud
plugin exposes useEnforceTypeSwitchGate, which OSS dynamically imports
with a no-op fallback. Blocked switches surface an alert and keep the
previous enforce_type.
* UN-2946 [FIX] Harden billing/usage paths against silent drops
- Guard embedding flush on callback_manager is None so public-adapter
embedding usage stops being silently swallowed by the broad except.
- Initialise LegacyExecutor._usage_records in __init__ so an
early-execute crash can't leave the orchestrator's getattr fallback
reading None.
- Wrap challenge and summarisation completion calls in try/finally so
flush_pending_usage() runs even on exception — transient errors
no longer drop those LLMs' billing rows.
- Replace bare except: pass on the FE error stream with
logger.debug; the secondary failure is now recoverable.
- Tighten UsageAPIClient.bulk_create_usage success heuristic against
future partial-body contracts; capture and log the bool return at the
Celery task with run_id + organization_id.
- Drop redundant organization_id kwarg passed alongside
set_organization_context in tasks.py.
- SDK records: spread _usage_kwargs first so explicit billing fields
win; write run_id/llm_usage_reason as None instead of "" so
UUIDField/choice columns don't reject the row.
- Use rsplit('/', 1)[-1] for display_model so multi-segment IDs
(e.g. bedrock/anthropic/claude) collapse to the trailing segment.
- Log the litellm cost_per_token failure for embeddings (matches the
LLM path).
* UN-2946 [FIX] Cross-cutting hygiene around lookup enrichment & webhook
- Tighten LookupEnrichmentProtocol to declare run_with_metrics
and METRICS_KEY so it actually matches the plugin call site —
run() -> None was structurally identical to the no-op protocols.
- Wrap the cloud run_with_metrics call in _run_lookup_enrichment
in a defensive try/except that logs + streams a WARN to the IDE log,
so plugin contract drift degrades the lookup gracefully instead of
aborting the answer-prompt batch.
- Hoist llm_cls out of the per-prompt hot loop — it was being
unpacked from a 7-tuple inside _run_lookup_enrichment on every
prompt; the caller already has it.
- Extract the is_empty ladder into a module-level _is_blank
helper so the predicate (and the falsy-but-valid 0/False rationale
comment) lives next to the predicate, not the executor.
- _run_webhook_postprocessing: when webhook_enabled and output
is non-JSON, log + shim.stream_log so the user sees the skip in
the IDE panel instead of silently never receiving the webhook call.
- Narrow persist_lookup_output catch to (IntegrityError,
ValidationError) and promote to logger.error — broad catches
were hiding plugin schema drift while reporting success.
- Wrap enrich_prompt_output in the prompt-output serializer's
to_representation so an enrichment exception no longer 500s the
list endpoint; matches the surrounding log-and-continue policy.
* UN-2946 [FIX] Tighten Usage choices & lookup_utils contracts
- UsageStatus(TextChoices) so the status field has an enforced
domain instead of free-text — producers (llm.py, usage_handler.py)
already write the canonical "SUCCESS" string so no producer changes
needed.
- Add usage_reference_pair_consistent CheckConstraint so a row
with reference_id set but reference_type NULL (or vice versa)
is rejected at the DB. Cheap to validate on apply because both fields
landed together in lookups V2 — legacy rows have both NULL. The
sibling (usage_type, llm_usage_reason) constraint is deferred to
a follow-up issue: legacy embedding rows have llm_usage_reason=''
from the old SDK default, and a full-table backfill or default
ADD CONSTRAINT scan would lock the billing table for too long in
prod.
- internal_views.py: write llm_usage_reason as None instead of
"" when missing so the choice column doesn't store an
out-of-domain value.
- lookup_utils.py: narrow the ImportError catch to the four cloud
lookup modules so a failing transitive import inside the cloud plugin
re-raises instead of silently degrading the whole feature to a no-op.
Annotate get_original_value_if_enriched return as
tuple[Any, dict] | None and rephrase the docstring to match the
actual shape callers tuple-unpack.
- Drop the attach_lookup_config /
attach_lookup_configs_to_tool_settings /
get_lookup_config_from_output wrappers — they were trivial dict
ops over a hardcoded key already used directly by the executor and
single-pass plugin, so the "key owned by the bridge" framing was
misleading. Inline at all five callsites.
- Drop the "future prompt_studio" forward-looking comment in
ide_callback/tasks.py — only source='lookup' is wired up, and
the cloud lookups plugin is the only registrant of the underlying
endpoints.
* UN-2946 [PERF] Push Combined Output queries into SQL
- latest_outputs_by_keys: switch the per-prompt latest pick to
order_by('prompt_id', '-modified_at').distinct('prompt_id') so
Postgres returns at most one row per prompt instead of materialising
every historical run + relying on a Python break.
- fetch_default_output_response: collapse the previous N+1
(exists() + for output in queryset per prompt) into a single
DISTINCT ON (prompt_id, profile_manager_id) query, plus a
per-tool cache for the default-profile lookup. Combined Output is a
hot path — the old shape made every panel switch O(prompts × runs)
with a plugin invocation per matching row.
- Drop the unused _resolve_profile_for_prompt and
_collect_default_output_for_prompt helpers, and the dead
except ObjectDoesNotExist: return '' (neither .exists() nor
the queryset iteration ever raised that exception).
* UN-2946 [FIX] Frontend & callback hygiene around lookup hooks
- PromptOutput.jsx: replace empty catch {} on the lookup
dynamic-import sites with console.warn so unexpected chunk-load
failures don't masquerade as OSS-mode behaviour. Add a
resolveCopyText helper that wraps getEnrichedCopyText in
try/catch + fallback so a plugin-side throw can't break the Copy
button at either of the two call sites.
- usePromptOutput.js: same catch (error) -> console.warn for
the two existing dynamic-import sites; wrap the per-item
handleLookupOutput call in try/catch so a single bad enrichment
payload no longer aborts the surrounding forEach and skips the
prompt-output state update.
- prompt_studio_core_v2/views.py: validate prompt_id before
the multi-var lookup gate so a missing field returns a clean 400
instead of a lookup-related error.
- ide_callback/tasks.py: (result_dict.get('data') or {}) so an
explicit data=None from the executor doesn't AttributeError into
a generic ERROR callback. Replace the inner except: pass swallow
with logger.debug so a secondary WS-emit failure during the
outer extraction_complete fallback is recoverable from logs.
* UN-2946 [FIX] Skip webhook on JSON parse failure & re-include compose.debug
- _run_webhook_postprocessing: skip when structured_output[prompt_name]
is empty or non-iterable. Pre-refactor, the webhook lived inside
handle_json after its parse-failure early-return (which sets the
output to {}), so a malformed JSON answer never dispatched the
webhook — the new explicit gate restores that behaviour. Subscribers
no longer receive empty-payload calls they didn't see before.
- .gitignore: re-include docker/compose.debug.yaml after the
broader docker/compose.*.yaml rule so a delete + recreate doesn't
make the tracked file look untracked, and so teammate-added compose
files aren't silently masked.
* UN-2946 [FIX] Return 400 for missing tool_id (was 500)
`APIException(code=400)` only sets `detail.code` in the JSON body;
`status_code` is hardcoded to HTTP_500_INTERNAL_SERVER_ERROR. Switch to
`ValidationError` so the missing-tool_id and tool-not-found branches in
`latest_outputs_by_keys` and `get_output_for_tool_default` actually
respond with 400 as the comment / docstring imply.
* UN-2946 [FIX] Address remaining post-disposition review comments (OSS)
Five threads on PR #1929 raised against the latest disposition push:
* prompt_studio_core_v2/views.py: drop the ``single_pass_extraction_mode``
bypass from ``_multi_var_lookup_block_response`` — fetch_response /
bulk_fetch_response are always non-SP, so the tool-setting check just
let multi-var lookups slip past the gate when the tool happened to be
configured for SP.
* prompt_studio_registry_helper.py: filter out NOTES + inactive prompts
*before* calling ``validate_lookups_for_export`` so an incomplete
lookup on a non-exportable prompt no longer fails the whole export.
* unstract/sdk1/usage_handler.py: guard ``self.token_counter is None``
in the embedding-end branch — degrade with a warning instead of an
AttributeError on early callbacks.
* workers/executor/legacy_executor.py: move the ``outcome.usage_records``
/ ``outcome.llm_metrics`` access inside the ``try`` so plugin contract
drift hits the same graceful-degrade branch as a thrown ``run_with_metrics``.
* backend/prompt_studio/lookup_utils.py: include ``pluggable_apps`` itself
in ``_CLOUD_LOOKUP_MODULES``. Pure OSS images don't have the parent
package, so ``ImportError.name`` surfaces as ``"pluggable_apps"`` and
the previous filter re-raised instead of setting LOOKUPS_AVAILABLE=False.
* UN-2946 [DOCS] Tighten comments across lookups V2 OSS surface
Drop archaeology / "previously / before-X-now-Y" framing; collapse
multi-line WHAT walkthroughs to single-line WHY. No logic changes.
* UN-2946 [REFACTOR] Drop unused token_count param from ExtractionAPIClient
Pairs with the cloud-side removal of the lookup token_count /
estimated_tokens fields — the worker no longer computes a value to send.
* UN-2946 [REFACTOR] DRF-ify Usage internal batch endpoint + squash migration
- UsageBatchCreateView raises DRF exceptions (drf-standardized-errors
envelope) instead of hand-rolled JsonResponse — serializer validation
via raise_exception=True, dedicated UsagePersistError(APIException)
for the bulk_create failure path.
- Records validated through UsageBatchCreateSerializer / nested
UsageRecordCreateSerializer so adapter_instance_id, model_name,
usage_type are required and the rest get explicit defaults.
- Fold 0006_alter_usage_status_and_more (UsageStatus choices +
reference_pair CheckConstraint) into 0004 — branch hasn't merged to
main, so squashing avoids an extra ALTER on deploy.
* UN-2946 [REFACTOR] Harden SDK / worker billing path + extract lookup helpers
- llm.py: token_counter fallback when prompt_tokens=0; rsplit('/',1) for
multi-segment provider IDs; spread _usage_kwargs first so explicit
billing fields win.
- utils/common.py: stamp every record appended during the call window
(was clobbering only the last entry).
- legacy_executor.py: extract run_lookup_enrichment / run_webhook_postprocessing
/ is_blank into workers/executor/executors/lookup_enrichment.py — caller
passes shim/state in, plugin returns usage_records for the caller to
extend its billing batch. Orchestrator stays pure dispatch.
- ide_callback/tasks.py: drop the char-÷4 token estimate heuristic;
context-manage the API clients so HTTP sessions don't leak.
* UN-2946 [FIX] Lookup-related FE polish + DRF error envelope
- prompt_studio/views.py: _multi_var_lookup_block_response uses
``detail`` instead of ``error`` to match drf-standardized-errors.
- CombinedOutput.jsx: hoist build helpers to module scope (selectedProfile
passed as arg) — no per-render allocation, fewer useEffect closures.
- DocumentParser.jsx: hoist UUID_RE to module scope (no per-render compile).
- PromptOutput.jsx: silent ``catch {}`` on plugin dynamic-import failures
so OSS doesn't surface noisy warnings for cloud-only modules.
- SideNavBar.jsx: hide the lookups submenu item when the lookup-studio
plugin isn't loaded (keeps OSS nav clean).
* UN-2946 [FIX] Type run_id / reference_id as UUIDField in batch serializer
Both columns are UUIDField on the Usage model — leaving them as
CharField in the serializer let invalid UUIDs slip through to bulk_create
and surface as a 500. UUIDField catches them at validation with a
standard DRF 400.
* UN-2946 [DOCS] Tighten extraction_complete docstring
Drops the stale "Computes token count" line — the callback no longer
derives a token count.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-2946 [FIX] Pre-bind validated_file_execution_id in usage client
If _validate_file_execution_id raised, the except handler hit
UnboundLocalError and masked the original ValueError. Pre-bind a
str(file_execution_id) fallback so the error response carries the
real cause. Also gitignore Codex's AGENTS.md scratchpad.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [CHORE] Ignore .pi/ tooling directory
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-3494 [REFACTOR] Replace polymorphic Usage attribution with typed columns + post-write hooks
Drop reference_id / reference_type from Usage in favour of typed
project_id / prompt_id columns indexed CONCURRENTLY for dashboard
rollups. Cloud-only attribution (e.g. lookup_version_id) now flows
through an opaque cloud_extras carrier on the batch endpoint, which
forwards it verbatim to plugin-registered post-write hooks invoked
inside the same atomic transaction as bulk_create — a hook failure
rolls back the Usage rows so attribution stays consistent or nothing
is written.
Removes the need for cloud to subclass UsageBatchCreateView or prepend
a URL override; the hook seam is generic for any future cloud feature.
* UN-3494 [REVIEW] Idempotent hook registration + lookup-usage partial index
- register_post_write_hook now dedupes by identity so AppConfig.ready()
re-firing under test reloads or dev autoreload can't queue a second
LookupUsage write that would IntegrityError and roll back the batch.
- usage_objects builder collapsed to a comprehension (review polish).
- New 0005 migration step adds a partial index
idx_usage_lookup_recent on (organization_id, created_at DESC)
WHERE llm_usage_reason='lookup', so the per-(run x prompt) dashboard
aggregation stops heap-scanning all Usage rows when filtering by
organisation + reason.
* UN-3494 [FIX] Forward answer-step metadata in structure pipeline
The SP cloud plugin returns its usage_records via ExecutionResult.metadata;
the non-SP path recovers them via self._usage_records in
LegacyExecutor.execute(). _handle_structure_pipeline only honoured the
second carrier — every SP-mode API deployment lost its extraction +
lookup billing rows silently because tasks.py guards the flush behind
``if usage_records:`` and the empty list short-circuits the post.
Forwarding answer_result.metadata closes the gap. Surface drift here
deserves a follow-up to consolidate to a single carrier; tracked
separately.
* UN-3494 [REFACTOR] Unify usage-records carrier and propagate execution_id
Collapses the dual-carrier pattern in the executor worker so every
handler returns its rows via ExecutionResult.metadata["usage_records"].
LegacyExecutor's instance attribute and recovery hook are removed; each
helper returns its records, orchestrators absorb child metadata into a
single list, and partial rows survive a mid-pipeline failure via
LegacyExecutorError.partial_usage_records. tasks.py now logs an INFO
line when an LLM-bearing op succeeds with zero rows so future drift
surfaces immediately.
Also fixes the long-standing dispatcher gap where structure_tool_task
omitted execution_id and file_execution_id when constructing
ExecutionContext for structure_pipeline / table_extract. The fields were
only stuffed inside executor_params, so context.execution_id was None
for every downstream handler. The legacy answer-prompt handler dug into
executor_params and worked, but SP plugin and summarize handlers
fell back to "" — and the dashboard's classifier mapped empty
execution_id to the IDE bucket. Setting the dataclass field at dispatch
plus reading context.execution_id in _handle_summarize lets workflow
rows classify as WF/API.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* UN-3494 [REVIEW] Trim verbose code comments
Drop WHAT-comments, references to PR/conversation context, and
multi-line explanations that didn't add WHY. Comments now describe
behavior generically so they make sense without prior context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 0538fdc commit f742e32
63 files changed
Lines changed: 2539 additions & 845 deletions
File tree
- backend
- prompt_studio
- prompt_studio_core_v2
- migrations
- prompt_studio_output_manager_v2
- prompt_studio_registry_v2
- prompt_studio_v2
- usage_v2
- migrations
- frontend/src
- components
- custom-tools
- combined-output
- document-parser
- header
- list-of-tools
- profile-info-bar
- prompt-card
- token-usage
- tool-ide
- helpers
- custom-markdown
- custom-tools
- logging/execution-logs
- widgets/empty-state
- helpers
- hooks
- pages
- routes
- unstract/sdk1/src/unstract/sdk1
- execution
- utils
- workers
- executor
- executors
- plugins
- file_processing
- ide_callback
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
653 | 653 | | |
654 | 654 | | |
655 | 655 | | |
656 | | - | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
657 | 661 | | |
658 | 662 | | |
659 | 663 | | |
| |||
696 | 700 | | |
697 | 701 | | |
698 | 702 | | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
699 | 709 | | |
700 | 710 | | |
701 | 711 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
Lines changed: 21 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
164 | 173 | | |
165 | 174 | | |
166 | 175 | | |
| |||
Lines changed: 23 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
23 | 28 | | |
24 | 29 | | |
25 | 30 | | |
| |||
387 | 392 | | |
388 | 393 | | |
389 | 394 | | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
390 | 398 | | |
391 | 399 | | |
392 | 400 | | |
| |||
798 | 806 | | |
799 | 807 | | |
800 | 808 | | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
801 | 812 | | |
802 | 813 | | |
803 | 814 | | |
| |||
1166 | 1177 | | |
1167 | 1178 | | |
1168 | 1179 | | |
| 1180 | + | |
| 1181 | + | |
| 1182 | + | |
| 1183 | + | |
1169 | 1184 | | |
1170 | 1185 | | |
1171 | 1186 | | |
| |||
1607 | 1622 | | |
1608 | 1623 | | |
1609 | 1624 | | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + | |
1610 | 1628 | | |
1611 | 1629 | | |
1612 | 1630 | | |
| |||
1672 | 1690 | | |
1673 | 1691 | | |
1674 | 1692 | | |
| 1693 | + | |
| 1694 | + | |
| 1695 | + | |
1675 | 1696 | | |
1676 | 1697 | | |
1677 | 1698 | | |
| |||
1911 | 1932 | | |
1912 | 1933 | | |
1913 | 1934 | | |
| 1935 | + | |
| 1936 | + | |
1914 | 1937 | | |
1915 | 1938 | | |
1916 | 1939 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
69 | 73 | | |
70 | 74 | | |
71 | 75 | | |
| |||
165 | 169 | | |
166 | 170 | | |
167 | 171 | | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
168 | 177 | | |
169 | 178 | | |
0 commit comments