* fix(api): validate pagination bounds on run-list endpoints (#553)
GET /workflow/{id}/runs and GET /campaign/{id}/runs declared bare
`page: int = 1` / `limit: int = 50` params, then computed
`total_pages = (total_count + limit - 1) // limit`. A `?limit=0` raised an
unhandled ZeroDivisionError (HTTP 500), and negative limit/page produced a
negative offset and nonsensical pagination.
Add `Query(ge=1, le=100)` / `Query(1, ge=1)` bounds to both endpoints,
matching the sibling list endpoints (/usage/runs and the superuser runs
endpoint) that already validate these. Out-of-range values now return 422.
Adds a regression test covering limit=0/-5/101 and page=0 on both endpoints.
Fixes#553
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(docs): regenerate openapi.json for run-list pagination bounds (#553)
The added Query(ge/le) bounds on the workflow-run and campaign-run list
endpoints changed the OpenAPI schema; regenerate the committed spec via
`python -m scripts.dump_docs_openapi` so the drift-check passes. Only the
limit/page parameter schemas for those two endpoints change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* fix: disable duplicate trigger nodes in workflow builder
AddNodePanel: disable trigger buttons and show tooltip when a trigger
already exists on the canvas, using bySpecName to identify trigger-
category specs from the live node list.
useWorkflowState: preflight in saveWorkflow rejects saves with multiple
trigger nodes via a sonner toast before the network request is made.
text_chat_session_service: include the original exception message in
TextChatSessionExecutionError so the HTTP 500 detail surfaces the root
cause without DB inspection.
Closes#378
* style: format test_text_chat_session_service.py with ruff
* chore: retrigger CI checks
* fix(workflow): enforce node instance constraints
---------
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* Validate workflow status filter to prevent 500 on invalid enum value
The /workflow/fetch and /workflow/summary endpoints accepted a free-form
status query param and passed it straight into a query that casts to the
workflow_status PG enum (active/archived). Any other value — e.g. an
external caller passing 'published' (a workflow_definitions version state,
not a workflow status) — failed deep in Postgres as
InvalidTextRepresentationError, surfacing as an unhandled HTTP 500.
Add _validate_status_filter() to reject values outside WorkflowStatus with
a clean 422 before any DB query, for both the single and comma-separated
paths. Add route tests covering invalid, valid-single, comma-separated, and
mixed valid/invalid cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: add tests
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: add model config v2
* chore: centralize user org selection
* chore: move preferences to platform settings
* fix: decouple org preference and ai model preferences
* Add Sarvam LLM provider, update Sarvam STT models, expose usage_info on run detail.
Depends on pipecat PR dograh-hq/pipecat#43 for STT string language support.
Submodule bump will follow after that merges.
* test: cover Sarvam STT language mapping; link Sarvam docs
---------
Co-authored-by: Sabiha Khan <sabihak89@gmail.com>
* fix: stamp API key into model override at save time to survive global provider change
When a workflow overrides the TTS/LLM/STT provider to match the current
global config, the override dict only stores model/voice fields, not the
API key. If the global config later switches to a different provider, the
override can no longer inherit the API key and calls fail.
Fix: enrich_overrides_with_api_keys() copies the global provider's API
key (and other secret fields) into the override dict at workflow-save
time, making the override self-contained regardless of future global
config changes.
* feat: add test coverage and masking logic
---------
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
If there are multiple telephony configurations, the form number should be initialized from the campaigns given telephonic configuration rather than the organization default telephonic configuration.
* feat: add tests and migrations
* feat: workflow versioning among published and draft
* feat: add a new settings page to simplify workflow detail page
* fix: fix tsclient generation