sea-dev 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/tasks/README.md +89 -0
- package/.cursor/rules/commits.mdc +31 -0
- package/.cursor/rules/general.mdc +84 -0
- package/.github/workflows/ci-cd.yml +141 -0
- package/CLAUDE.md +337 -0
- package/README.md +129 -0
- package/apps/api/.prettierignore +6 -0
- package/apps/api/.prettierrc.js +3 -0
- package/apps/api/dotenvx-safe.sh +11 -0
- package/apps/api/eslint.config.mjs +3 -0
- package/apps/api/package.json +58 -0
- package/apps/api/src/clients/posthog.ts +25 -0
- package/apps/api/src/dal/submission.ts +59 -0
- package/apps/api/src/errors.ts +55 -0
- package/apps/api/src/index.ts +21 -0
- package/apps/api/src/lib/channel.ts +28 -0
- package/apps/api/src/lib/config.ts +9 -0
- package/apps/api/src/lib/fmt.test.ts +9 -0
- package/apps/api/src/lib/fmt.ts +62 -0
- package/apps/api/src/lib/invariant.ts +23 -0
- package/apps/api/src/middleware/auth.ts +66 -0
- package/apps/api/src/routes/index.ts +20 -0
- package/apps/api/src/routes/v2/chat/handlers.ts +693 -0
- package/apps/api/src/routes/v2/chat/index.ts +257 -0
- package/apps/api/src/routes/v2/chat/schemas.ts +43 -0
- package/apps/api/src/routes/v2/deals/handlers.ts +64 -0
- package/apps/api/src/routes/v2/deals/index.ts +88 -0
- package/apps/api/src/routes/v2/deals/schemas.ts +38 -0
- package/apps/api/src/routes/v2/forms/handlers.ts +415 -0
- package/apps/api/src/routes/v2/forms/index.ts +382 -0
- package/apps/api/src/routes/v2/forms/schemas.ts +243 -0
- package/apps/api/src/routes/v2/index.ts +19 -0
- package/apps/api/src/routes/v2/pipelines/handlers.ts +261 -0
- package/apps/api/src/routes/v2/pipelines/index.ts +224 -0
- package/apps/api/src/routes/v2/pipelines/schemas.ts +173 -0
- package/apps/api/src/routes/v2/submissions/handlers.ts +555 -0
- package/apps/api/src/routes/v2/submissions/index.ts +366 -0
- package/apps/api/src/routes/v2/submissions/schemas.ts +233 -0
- package/apps/api/src/routes/v2/workflows/handlers.ts +81 -0
- package/apps/api/src/routes/v2/workflows/index.ts +88 -0
- package/apps/api/src/routes/v2/workflows/schemas.ts +40 -0
- package/apps/api/src/server.ts +146 -0
- package/apps/api/src/static/favicon.ico +0 -0
- package/apps/api/src/types/api.ts +14 -0
- package/apps/api/src/types/result.ts +3 -0
- package/apps/api/tsconfig.json +22 -0
- package/apps/api/vite.config.ts +28 -0
- package/apps/api/vitest.config.ts +14 -0
- package/apps/conversion-worker/Dockerfile +59 -0
- package/apps/conversion-worker/package.json +31 -0
- package/apps/conversion-worker/src/lib/config.ts +7 -0
- package/apps/conversion-worker/src/main.ts +22 -0
- package/apps/conversion-worker/src/workflows/convert-pptx.ts +116 -0
- package/apps/conversion-worker/tsconfig.json +27 -0
- package/apps/conversion-worker/vite.config.ts +33 -0
- package/apps/main/.prettierignore +6 -0
- package/apps/main/.prettierrc.js +3 -0
- package/apps/main/CLAUDE.md +245 -0
- package/apps/main/Procfile +1 -0
- package/apps/main/README.md +193 -0
- package/apps/main/db-tests.jsonl +116 -0
- package/apps/main/dotenvx-safe.sh +11 -0
- package/apps/main/drizzle/meta/_journal.json +1 -0
- package/apps/main/drizzle.config.ts +25 -0
- package/apps/main/eslint.config.mjs +3 -0
- package/apps/main/generate-routes.mjs +5 -0
- package/apps/main/package.json +131 -0
- package/apps/main/playwright.config.ts +23 -0
- package/apps/main/postcss.config.ts +5 -0
- package/apps/main/public/bg-dark.svg +10 -0
- package/apps/main/public/bg.svg +10 -0
- package/apps/main/public/favicon.ico +0 -0
- package/apps/main/run.sh +146 -0
- package/apps/main/scripts/browser.ts +14 -0
- package/apps/main/scripts/db-test-cov.ts +277 -0
- package/apps/main/scripts/login.ts +78 -0
- package/apps/main/scripts/repl.ts +61 -0
- package/apps/main/src/_foo.ts +31 -0
- package/apps/main/src/_tests/db.test.ts +19 -0
- package/apps/main/src/_tests/mock-db.ts +60 -0
- package/apps/main/src/client.tsx +13 -0
- package/apps/main/src/clients/loops.ts +13 -0
- package/apps/main/src/clients/polar.ts +12 -0
- package/apps/main/src/clients/posthog.ts +12 -0
- package/apps/main/src/components/chat/chat-context.tsx +99 -0
- package/apps/main/src/components/chat/chat-messages.tsx +184 -0
- package/apps/main/src/components/chat/chat-status.tsx +140 -0
- package/apps/main/src/components/chat/chat.tsx +458 -0
- package/apps/main/src/components/chat/citation-modal.tsx +54 -0
- package/apps/main/src/components/cta.tsx +21 -0
- package/apps/main/src/components/data-display/derived.tsx +40 -0
- package/apps/main/src/components/data-display/group-single.tsx +57 -0
- package/apps/main/src/components/data-display/group-table.tsx +165 -0
- package/apps/main/src/components/data-display/group-wrapper.tsx +54 -0
- package/apps/main/src/components/data-display/item.tsx +678 -0
- package/apps/main/src/components/error.tsx +45 -0
- package/apps/main/src/components/forms/error.tsx +22 -0
- package/apps/main/src/components/grid.tsx +7 -0
- package/apps/main/src/components/header/container.tsx +73 -0
- package/apps/main/src/components/header/header-bar.tsx +102 -0
- package/apps/main/src/components/modals/copy-display.tsx +37 -0
- package/apps/main/src/components/modals/copy-form.tsx +152 -0
- package/apps/main/src/components/modals/duplicate-workflow.tsx +89 -0
- package/apps/main/src/components/modals/field-correction.tsx +323 -0
- package/apps/main/src/components/modals/form-viewer.tsx +126 -0
- package/apps/main/src/components/modals/modals.tsx +44 -0
- package/apps/main/src/components/modals/new-deal.tsx +78 -0
- package/apps/main/src/components/modals/new-form.tsx +133 -0
- package/apps/main/src/components/modals/new-pipeline.tsx +70 -0
- package/apps/main/src/components/modals/new-submission.tsx +321 -0
- package/apps/main/src/components/modals/new-workflow.tsx +342 -0
- package/apps/main/src/components/modals/transformation-sources-modal.tsx +157 -0
- package/apps/main/src/components/modals/view-report.tsx +193 -0
- package/apps/main/src/components/not-found.tsx +14 -0
- package/apps/main/src/components/search/search-bar.tsx +178 -0
- package/apps/main/src/components/sheet-selector.tsx +135 -0
- package/apps/main/src/components/side-panel/doc-list.tsx +480 -0
- package/apps/main/src/components/sidebar/admin-sidebar.tsx +75 -0
- package/apps/main/src/components/sidebar/app-sidebar.tsx +417 -0
- package/apps/main/src/components/sidebar/model-select.tsx +134 -0
- package/apps/main/src/components/sidebar/settings-sidebar.tsx +132 -0
- package/apps/main/src/components/sidebar/sidebar-right.tsx +22 -0
- package/apps/main/src/components/sidebar/stop-impersonate.tsx +21 -0
- package/apps/main/src/components/svg/loading.tsx +33 -0
- package/apps/main/src/components/theme-selector.tsx +43 -0
- package/apps/main/src/components/unsaved-badge.tsx +19 -0
- package/apps/main/src/components/upload/file-upload.tsx +354 -0
- package/apps/main/src/fns/submission-groups.ts +28 -0
- package/apps/main/src/fns/submission-items.ts +11 -0
- package/apps/main/src/global-middleware.ts +16 -0
- package/apps/main/src/hooks/use-update-state.ts +18 -0
- package/apps/main/src/lib/auth-client.ts +16 -0
- package/apps/main/src/lib/auth.test.ts +359 -0
- package/apps/main/src/lib/auth.ts +144 -0
- package/apps/main/src/lib/billing.ts +23 -0
- package/apps/main/src/lib/config-iso.ts +76 -0
- package/apps/main/src/lib/config.ts +61 -0
- package/apps/main/src/lib/excel.ts +16 -0
- package/apps/main/src/lib/feedback-cache.ts +70 -0
- package/apps/main/src/lib/logger.ts +44 -0
- package/apps/main/src/lib/models.ts +22 -0
- package/apps/main/src/lib/not-found.ts +17 -0
- package/apps/main/src/lib/pdf.ts +16 -0
- package/apps/main/src/lib/tabularize.ts +54 -0
- package/apps/main/src/lib/utils.ts +10 -0
- package/apps/main/src/lib/zfd.ts +217 -0
- package/apps/main/src/middleware.ts +55 -0
- package/apps/main/src/routeTree.gen.ts +1255 -0
- package/apps/main/src/router.tsx +24 -0
- package/apps/main/src/routes/__root.tsx +92 -0
- package/apps/main/src/routes/_authed/_app/(dashboard)/index.tsx +227 -0
- package/apps/main/src/routes/_authed/_app/agents/$agentId/config.tsx +224 -0
- package/apps/main/src/routes/_authed/_app/agents/$agentId/index.tsx +206 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-actions-menu.tsx +94 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-artifacts.tsx +153 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-chat.tsx +220 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-history-menu.tsx +81 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-model-select.tsx +84 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-relevant-items.tsx +226 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-upload-button.tsx +298 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/context-modal.tsx +187 -0
- package/apps/main/src/routes/_authed/_app/agents/-fns.ts +560 -0
- package/apps/main/src/routes/_authed/_app/agents/index.tsx +65 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId/-components/citation-tree.tsx +268 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId.tsx +655 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/doc-loading.tsx +37 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/share-link.tsx +42 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-card.tsx +89 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-filter.tsx +193 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submissions.tsx +36 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/summary.tsx +82 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/upload-doc.tsx +120 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-fns.ts +653 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/index.tsx +259 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/route.tsx +29 -0
- package/apps/main/src/routes/_authed/_app/deals/index.tsx +104 -0
- package/apps/main/src/routes/_authed/_app/feedback/index.tsx +639 -0
- package/apps/main/src/routes/_authed/_app/feedback/insights.tsx +250 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/blockers-panel.tsx +260 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/manual-input-panel.tsx +301 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/submission-selector-modal.tsx +143 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/upload-doc.tsx +120 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/index.tsx +1485 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/dag-view.tsx +296 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/step-config-modal.tsx +634 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/index.tsx +911 -0
- package/apps/main/src/routes/_authed/_app/pipelines/-fns.ts +510 -0
- package/apps/main/src/routes/_authed/_app/pipelines/index.tsx +103 -0
- package/apps/main/src/routes/_authed/_app/reports/$reportId.tsx +397 -0
- package/apps/main/src/routes/_authed/_app/reports/-fns.ts +11 -0
- package/apps/main/src/routes/_authed/_app/reports/index.tsx +22 -0
- package/apps/main/src/routes/_authed/_app/route.tsx +48 -0
- package/apps/main/src/routes/_authed/_app/submissions/-columns.tsx +161 -0
- package/apps/main/src/routes/_authed/_app/submissions/-fns.ts +128 -0
- package/apps/main/src/routes/_authed/_app/submissions/index.tsx +190 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/$formId.tsx +542 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/derived.tsx +154 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/field.tsx +369 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/group.tsx +475 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/index.tsx +263 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/route.tsx +33 -0
- package/apps/main/src/routes/_authed/_app/workflows/-components/form-card.tsx +315 -0
- package/apps/main/src/routes/_authed/_app/workflows/index.tsx +86 -0
- package/apps/main/src/routes/_authed/admin/index.tsx +12 -0
- package/apps/main/src/routes/_authed/admin/route.tsx +42 -0
- package/apps/main/src/routes/_authed/admin/users/-columns.tsx +124 -0
- package/apps/main/src/routes/_authed/admin/users/-fns.ts +30 -0
- package/apps/main/src/routes/_authed/admin/users/index.tsx +29 -0
- package/apps/main/src/routes/_authed/catchNotFound.tsx +114 -0
- package/apps/main/src/routes/_authed/redirects/forms.$id.tsx +29 -0
- package/apps/main/src/routes/_authed/redirects/submissions.$id.tsx +27 -0
- package/apps/main/src/routes/_authed/redirects/workflows.$id.tsx +27 -0
- package/apps/main/src/routes/_authed/route.tsx +51 -0
- package/apps/main/src/routes/_authed/settings/-components/new-api-key.tsx +85 -0
- package/apps/main/src/routes/_authed/settings/-components/new-invite.tsx +100 -0
- package/apps/main/src/routes/_authed/settings/analytics.tsx +1710 -0
- package/apps/main/src/routes/_authed/settings/billing/-components/price-table.tsx +129 -0
- package/apps/main/src/routes/_authed/settings/billing/-fns.ts +76 -0
- package/apps/main/src/routes/_authed/settings/billing/index.tsx +119 -0
- package/apps/main/src/routes/_authed/settings/embed.tsx +337 -0
- package/apps/main/src/routes/_authed/settings/index.tsx +12 -0
- package/apps/main/src/routes/_authed/settings/keys.tsx +157 -0
- package/apps/main/src/routes/_authed/settings/members.tsx +276 -0
- package/apps/main/src/routes/_authed/settings/route.tsx +22 -0
- package/apps/main/src/routes/_authed/settings/user.tsx +87 -0
- package/apps/main/src/routes/_authed/settings/workspace.tsx +206 -0
- package/apps/main/src/routes/_public/-components/sign-in-up.tsx +96 -0
- package/apps/main/src/routes/_public/embedded.tsx +57 -0
- package/apps/main/src/routes/_public/invite.$inviteId.tsx +143 -0
- package/apps/main/src/routes/_public/no-access.tsx +38 -0
- package/apps/main/src/routes/_public/no-invite.tsx +39 -0
- package/apps/main/src/routes/_public/otp.tsx +103 -0
- package/apps/main/src/routes/_public/route.tsx +15 -0
- package/apps/main/src/routes/_public/sign-in.tsx +111 -0
- package/apps/main/src/routes/_public/sign-up.tsx +114 -0
- package/apps/main/src/routes/api/auth/$.ts +11 -0
- package/apps/main/src/routes/api/billing/paid.ts +42 -0
- package/apps/main/src/routes/api/billing/webhooks.ts +70 -0
- package/apps/main/src/routes/api/chat/agent.ts +40 -0
- package/apps/main/src/routes/api/chat/key.ts +42 -0
- package/apps/main/src/routes/api/chat/member.ts +35 -0
- package/apps/main/src/routes/api/test/index.ts +19 -0
- package/apps/main/src/server.tsx +6 -0
- package/apps/main/src/styles/app.css +23 -0
- package/apps/main/src/vite-env.d.ts +7 -0
- package/apps/main/test.http +6 -0
- package/apps/main/tsconfig.json +17 -0
- package/apps/main/vite.config.ts +24 -0
- package/apps/main/vitest.config.js +17 -0
- package/apps/mcp/README.md +171 -0
- package/apps/mcp/eslint.config.mjs +3 -0
- package/apps/mcp/package.json +37 -0
- package/apps/mcp/src/index.ts +414 -0
- package/apps/mcp/tsconfig.json +19 -0
- package/apps/mcp/vite.config.ts +22 -0
- package/apps/posthog-proxy/index.html +9 -0
- package/apps/workers/.prettierignore +7 -0
- package/apps/workers/.prettierrc.js +3 -0
- package/apps/workers/dotenvx-safe.sh +11 -0
- package/apps/workers/eslint.config.mjs +3 -0
- package/apps/workers/package.json +65 -0
- package/apps/workers/src/lib/config.ts +7 -0
- package/apps/workers/src/lib/messages.ts +0 -0
- package/apps/workers/src/lib/posthog.ts +25 -0
- package/apps/workers/src/main.ts +58 -0
- package/apps/workers/src/workflows/extraction.ts +866 -0
- package/apps/workers/src/workflows/index.ts +3 -0
- package/apps/workers/src/workflows/pipeline-dag.ts +210 -0
- package/apps/workers/src/workflows/pipeline-steps.ts +1393 -0
- package/apps/workers/tsconfig.json +16 -0
- package/apps/workers/vite.config.ts +35 -0
- package/docs/CHANGELOG.md +84 -0
- package/docs/agent-templates-and-runs.md +859 -0
- package/docs/aws-migration-plan.md +267 -0
- package/docs/impl-p0-form-builder-improvements.md +683 -0
- package/docs/on-prem-deployment-spec.docx +0 -0
- package/docs/on-prem-deployment-spec.md +378 -0
- package/docs/prd-form-builder-strategy.md +1120 -0
- package/docs/widget-ng-apf-packaging-spec.md +43 -0
- package/infra/k8s/charts/seadotdev/Chart.yaml +6 -0
- package/infra/k8s/charts/seadotdev/templates/_helpers.tpl +27 -0
- package/infra/k8s/charts/seadotdev/templates/api-v2.yaml +105 -0
- package/infra/k8s/charts/seadotdev/templates/external-secrets.yaml +83 -0
- package/infra/k8s/charts/seadotdev/templates/ingress.yaml +54 -0
- package/infra/k8s/charts/seadotdev/templates/main-app.yaml +104 -0
- package/infra/k8s/charts/seadotdev/templates/workers.yaml +182 -0
- package/infra/k8s/charts/seadotdev/values.yaml +143 -0
- package/infra/terraform/main.tf +399 -0
- package/libs/ai/.prettierignore +2 -0
- package/libs/ai/.prettierrc.js +5 -0
- package/libs/ai/README.md +139 -0
- package/libs/ai/eslint.config.mjs +3 -0
- package/libs/ai/package.json +42 -0
- package/libs/ai/src/index.ts +5 -0
- package/libs/ai/src/models.ts +19 -0
- package/libs/ai/src/rag/index.ts +1 -0
- package/libs/ai/src/rag/rag.test.ts +99 -0
- package/libs/ai/src/rag/rag.ts +510 -0
- package/libs/ai/tsconfig.json +21 -0
- package/libs/ai/vite.config.ts +38 -0
- package/libs/cache/.prettierignore +2 -0
- package/libs/cache/eslint.config.mjs +3 -0
- package/libs/cache/package.json +35 -0
- package/libs/cache/src/feedback.ts +77 -0
- package/libs/cache/src/index.ts +2 -0
- package/libs/cache/tsconfig.json +19 -0
- package/libs/cache/vite.config.ts +36 -0
- package/libs/clients/.prettierignore +6 -0
- package/libs/clients/eslint.config.mjs +3 -0
- package/libs/clients/package.json +59 -0
- package/libs/clients/src/azure.ts +249 -0
- package/libs/clients/src/gcp.ts +220 -0
- package/libs/clients/src/hatchet.ts +86 -0
- package/libs/clients/src/index.ts +8 -0
- package/libs/clients/src/loops.ts +86 -0
- package/libs/clients/src/polar.ts +77 -0
- package/libs/clients/src/posthog.ts +55 -0
- package/libs/clients/tsconfig.json +19 -0
- package/libs/clients/vite.config.ts +35 -0
- package/libs/config/.prettierignore +6 -0
- package/libs/config/.prettierrc.js +12 -0
- package/libs/config/eslint.config.mjs +3 -0
- package/libs/config/package.json +50 -0
- package/libs/config/src/azure.ts +54 -0
- package/libs/config/src/db.ts +18 -0
- package/libs/config/src/gcp.ts +53 -0
- package/libs/config/src/google.ts +17 -0
- package/libs/config/src/hatchet.ts +20 -0
- package/libs/config/src/index.ts +108 -0
- package/libs/config/src/llm.ts +17 -0
- package/libs/config/src/polar.ts +24 -0
- package/libs/config/src/util.ts +8 -0
- package/libs/config/src/vercel.ts +26 -0
- package/libs/config/tsconfig.json +19 -0
- package/libs/config/vite.config.ts +34 -0
- package/libs/core/.prettierignore +2 -0
- package/libs/core/eslint.config.mjs +3 -0
- package/libs/core/package.json +59 -0
- package/libs/core/src/chat/derived.ts +97 -0
- package/libs/core/src/chat/feedback.ts +293 -0
- package/libs/core/src/chat/index.ts +6 -0
- package/libs/core/src/chat/model.ts +92 -0
- package/libs/core/src/chat/prepare-tools.ts +286 -0
- package/libs/core/src/chat/prompts.ts +623 -0
- package/libs/core/src/chat/stream.ts +311 -0
- package/libs/core/src/chat/summarize.ts +168 -0
- package/libs/core/src/chat/tools/agent.ts +403 -0
- package/libs/core/src/chat/tools/chart-agent.ts +526 -0
- package/libs/core/src/chat/tools/chart-helpers/sandbox.ts +47 -0
- package/libs/core/src/chat/tools/chart.ts +86 -0
- package/libs/core/src/chat/tools/credit-agent.ts +1383 -0
- package/libs/core/src/chat/tools/credit.ts +1435 -0
- package/libs/core/src/chat/tools/deep-dive-agent.ts +100 -0
- package/libs/core/src/chat/tools/deep-dive.ts +141 -0
- package/libs/core/src/chat/tools/form.ts +449 -0
- package/libs/core/src/chat/tools/helpers.ts +91 -0
- package/libs/core/src/chat/tools/index.ts +42 -0
- package/libs/core/src/chat/tools/pipeline-artifact.ts +76 -0
- package/libs/core/src/chat/tools/report.ts +40 -0
- package/libs/core/src/chat/tools/search.ts +390 -0
- package/libs/core/src/chat/tools/submission.ts +227 -0
- package/libs/core/src/chat/tools/workflow.ts +684 -0
- package/libs/core/src/chat/types.ts +3 -0
- package/libs/core/src/data-extraction/classification/azure.ts +168 -0
- package/libs/core/src/data-extraction/classification/index.ts +1 -0
- package/libs/core/src/data-extraction/dal.ts +246 -0
- package/libs/core/src/data-extraction/form-structure-extractor.ts +294 -0
- package/libs/core/src/data-extraction/index.ts +4 -0
- package/libs/core/src/data-extraction/layout/azure.ts +730 -0
- package/libs/core/src/data-extraction/layout/excel.ts +180 -0
- package/libs/core/src/data-extraction/layout/gcp.ts +1071 -0
- package/libs/core/src/data-extraction/layout/index.ts +266 -0
- package/libs/core/src/data-extraction/layout/plaintext.ts +45 -0
- package/libs/core/src/data-extraction/models.ts +38 -0
- package/libs/core/src/data-extraction/pdf-utils.ts +96 -0
- package/libs/core/src/data-extraction/structuring/bank-statement.ts +1182 -0
- package/libs/core/src/data-extraction/structuring/custom.ts +495 -0
- package/libs/core/src/data-extraction/structuring/index.ts +290 -0
- package/libs/core/src/data-extraction/structuring/prompts.ts +69 -0
- package/libs/core/src/data-extraction/type-guards.ts +110 -0
- package/libs/core/src/data-extraction/types.ts +84 -0
- package/libs/core/src/data-extraction/utils.ts +31 -0
- package/libs/core/src/data-extraction/validation/bank-statement.ts +127 -0
- package/libs/core/src/deals.ts +17 -0
- package/libs/core/src/documents.ts +152 -0
- package/libs/core/src/index.ts +5 -0
- package/libs/core/src/pipelines/display.ts +678 -0
- package/libs/core/src/pipelines/execute.ts +2342 -0
- package/libs/core/src/pipelines/index.ts +4 -0
- package/libs/core/src/pipelines/list.ts +12 -0
- package/libs/core/src/pipelines/runs.ts +53 -0
- package/libs/core/tsconfig.json +20 -0
- package/libs/core/vite.config.ts +56 -0
- package/libs/dal/.prettierignore +6 -0
- package/libs/dal/.prettierrc.js +12 -0
- package/libs/dal/eslint.config.mjs +3 -0
- package/libs/dal/package.json +57 -0
- package/libs/dal/src/_tests/db.test.ts +19 -0
- package/libs/dal/src/_tests/mock-db.ts +60 -0
- package/libs/dal/src/api-key.test.ts +397 -0
- package/libs/dal/src/api-key.ts +110 -0
- package/libs/dal/src/billing.ts +23 -0
- package/libs/dal/src/conversation.test.ts +655 -0
- package/libs/dal/src/conversation.ts +532 -0
- package/libs/dal/src/deal.test.ts +45 -0
- package/libs/dal/src/deal.ts +87 -0
- package/libs/dal/src/defaults-consumer-lending-uk.ts +33 -0
- package/libs/dal/src/defaults-consumer-lending-us.ts +33 -0
- package/libs/dal/src/defaults-private-credit.ts +57 -0
- package/libs/dal/src/defaults-private-equity.ts +51 -0
- package/libs/dal/src/defaults-smb-lending-us.ts +1569 -0
- package/libs/dal/src/defaults-sme-lending-uk-express.ts +1527 -0
- package/libs/dal/src/defaults-sme-lending-uk.ts +1669 -0
- package/libs/dal/src/defaults-types.ts +23 -0
- package/libs/dal/src/defaults.ts +550 -0
- package/libs/dal/src/document.test.ts +70 -0
- package/libs/dal/src/document.ts +192 -0
- package/libs/dal/src/feedback.ts +255 -0
- package/libs/dal/src/form.test.ts +637 -0
- package/libs/dal/src/form.ts +1165 -0
- package/libs/dal/src/index.ts +20 -0
- package/libs/dal/src/invitation.test.ts +746 -0
- package/libs/dal/src/invitation.ts +207 -0
- package/libs/dal/src/member.test.ts +185 -0
- package/libs/dal/src/member.ts +80 -0
- package/libs/dal/src/organization.ts +116 -0
- package/libs/dal/src/permission.ts +25 -0
- package/libs/dal/src/pipeline.test.ts +388 -0
- package/libs/dal/src/pipeline.ts +4222 -0
- package/libs/dal/src/report.ts +199 -0
- package/libs/dal/src/result.ts +16 -0
- package/libs/dal/src/search.ts +172 -0
- package/libs/dal/src/session.test.ts +110 -0
- package/libs/dal/src/session.ts +31 -0
- package/libs/dal/src/submission.test.ts +1304 -0
- package/libs/dal/src/submission.ts +1396 -0
- package/libs/dal/src/tool.ts +159 -0
- package/libs/dal/src/user.ts +16 -0
- package/libs/dal/src/workflow.test.ts +89 -0
- package/libs/dal/src/workflow.ts +262 -0
- package/libs/dal/tsconfig.build.json +4 -0
- package/libs/dal/tsconfig.json +22 -0
- package/libs/dal/vite.config.ts +34 -0
- package/libs/db/.prettierignore +6 -0
- package/libs/db/.prettierrc.js +12 -0
- package/libs/db/eslint.config.mjs +3 -0
- package/libs/db/package.json +52 -0
- package/libs/db/src/index.ts +24 -0
- package/libs/db/src/relations.ts +549 -0
- package/libs/db/src/schema.ts +2 -0
- package/libs/db/src/schemas/api.ts +35 -0
- package/libs/db/src/schemas/conversations.ts +175 -0
- package/libs/db/src/schemas/core.ts +359 -0
- package/libs/db/src/schemas/documents.ts +181 -0
- package/libs/db/src/schemas/feedback.ts +40 -0
- package/libs/db/src/schemas/index.ts +26 -0
- package/libs/db/src/schemas/organisations.ts +97 -0
- package/libs/db/src/schemas/pipelines.ts +440 -0
- package/libs/db/src/schemas/users.ts +95 -0
- package/libs/db/src/types.ts +190 -0
- package/libs/db/src/utils.ts +14 -0
- package/libs/db/tsconfig.json +19 -0
- package/libs/db/vite.config.ts +31 -0
- package/libs/lint/.prettierignore +6 -0
- package/libs/lint/eslint.config.mjs +61 -0
- package/libs/lint/package.json +29 -0
- package/libs/lint/prettier.config.js +12 -0
- package/libs/schemas/.prettierignore +6 -0
- package/libs/schemas/.prettierrc.js +12 -0
- package/libs/schemas/README.md +15 -0
- package/libs/schemas/eslint.config.mjs +3 -0
- package/libs/schemas/package.json +67 -0
- package/libs/schemas/src/core/chat.ts +67 -0
- package/libs/schemas/src/core/core-result.ts +15 -0
- package/libs/schemas/src/core/data-extraction.ts +184 -0
- package/libs/schemas/src/core/layout.ts +478 -0
- package/libs/schemas/src/core/pipeline.ts +128 -0
- package/libs/schemas/src/core/submission.ts +97 -0
- package/libs/schemas/src/db/account.ts +57 -0
- package/libs/schemas/src/db/apiKey.ts +57 -0
- package/libs/schemas/src/db/context.ts +33 -0
- package/libs/schemas/src/db/conversation.ts +65 -0
- package/libs/schemas/src/db/deal.ts +42 -0
- package/libs/schemas/src/db/document.ts +103 -0
- package/libs/schemas/src/db/documentCitation.ts +58 -0
- package/libs/schemas/src/db/documentExtraction.ts +69 -0
- package/libs/schemas/src/db/fieldCorrection.ts +85 -0
- package/libs/schemas/src/db/form.ts +45 -0
- package/libs/schemas/src/db/formField.ts +59 -0
- package/libs/schemas/src/db/formGroup.ts +42 -0
- package/libs/schemas/src/db/impersonation.ts +39 -0
- package/libs/schemas/src/db/index.ts +25 -0
- package/libs/schemas/src/db/invitation.ts +42 -0
- package/libs/schemas/src/db/member.ts +36 -0
- package/libs/schemas/src/db/message.ts +58 -0
- package/libs/schemas/src/db/organization.ts +62 -0
- package/libs/schemas/src/db/session.ts +48 -0
- package/libs/schemas/src/db/submission.ts +54 -0
- package/libs/schemas/src/db/submissionGroup.ts +36 -0
- package/libs/schemas/src/db/submissionItem.ts +33 -0
- package/libs/schemas/src/db/submissionItemVersion.ts +70 -0
- package/libs/schemas/src/db/user.ts +51 -0
- package/libs/schemas/src/db/utils.ts +3 -0
- package/libs/schemas/src/db/verification.ts +36 -0
- package/libs/schemas/src/db/workflow.ts +42 -0
- package/libs/schemas/src/index.ts +10 -0
- package/libs/schemas/tsconfig.json +21 -0
- package/libs/schemas/vite.config.ts +38 -0
- package/libs/ui/.prettierignore +6 -0
- package/libs/ui/.prettierrc.js +12 -0
- package/libs/ui/components.json +24 -0
- package/libs/ui/eslint.config.mjs +3 -0
- package/libs/ui/package.json +142 -0
- package/libs/ui/src/components/chart-viz/chart.tsx +255 -0
- package/libs/ui/src/components/chart-viz/converters.ts +474 -0
- package/libs/ui/src/components/chart-viz/dashboard.tsx +146 -0
- package/libs/ui/src/components/chart-viz/index.ts +37 -0
- package/libs/ui/src/components/chart-viz/markdown.tsx +344 -0
- package/libs/ui/src/components/chart-viz/table.tsx +446 -0
- package/libs/ui/src/components/chart-viz/theme-context.tsx +70 -0
- package/libs/ui/src/components/chart-viz/themes/dark.ts +98 -0
- package/libs/ui/src/components/chart-viz/themes/index.ts +69 -0
- package/libs/ui/src/components/chart-viz/themes/light.ts +98 -0
- package/libs/ui/src/components/chart-viz/themes/tailwind.ts +326 -0
- package/libs/ui/src/components/chart-viz/themes/types.ts +99 -0
- package/libs/ui/src/components/chart-viz/tool-display.tsx +150 -0
- package/libs/ui/src/components/chart-viz/types.ts +95 -0
- package/libs/ui/src/components/doc-viewers/excel/index.tsx +431 -0
- package/libs/ui/src/components/doc-viewers/excel/themes.ts +160 -0
- package/libs/ui/src/components/doc-viewers/image/index.tsx +410 -0
- package/libs/ui/src/components/doc-viewers/pdf/index.tsx +258 -0
- package/libs/ui/src/components/doc-viewers/pdf/virtualized-pdf.tsx +556 -0
- package/libs/ui/src/components/misc/rel-date.tsx +52 -0
- package/libs/ui/src/components/misc/styled-link.tsx +2 -0
- package/libs/ui/src/components/table/data-table.tsx +546 -0
- package/libs/ui/src/components/table/report-table.tsx +305 -0
- package/libs/ui/src/components/table/sortable-column.tsx +34 -0
- package/libs/ui/src/components/ui/accordion.tsx +62 -0
- package/libs/ui/src/components/ui/alert-dialog.tsx +142 -0
- package/libs/ui/src/components/ui/alert.tsx +62 -0
- package/libs/ui/src/components/ui/artifact.tsx +118 -0
- package/libs/ui/src/components/ui/attachments.tsx +388 -0
- package/libs/ui/src/components/ui/avatar.tsx +39 -0
- package/libs/ui/src/components/ui/badge.tsx +43 -0
- package/libs/ui/src/components/ui/breadcrumb.tsx +102 -0
- package/libs/ui/src/components/ui/button-group.tsx +78 -0
- package/libs/ui/src/components/ui/button.tsx +79 -0
- package/libs/ui/src/components/ui/card.tsx +32 -0
- package/libs/ui/src/components/ui/carousel.tsx +228 -0
- package/libs/ui/src/components/ui/chain-of-thought.tsx +198 -0
- package/libs/ui/src/components/ui/checkbox.tsx +27 -0
- package/libs/ui/src/components/ui/citation.tsx +34 -0
- package/libs/ui/src/components/ui/code-block.tsx +500 -0
- package/libs/ui/src/components/ui/collapsible.tsx +19 -0
- package/libs/ui/src/components/ui/command.tsx +161 -0
- package/libs/ui/src/components/ui/conversation.tsx +90 -0
- package/libs/ui/src/components/ui/dialog.tsx +142 -0
- package/libs/ui/src/components/ui/dropdown-menu.tsx +246 -0
- package/libs/ui/src/components/ui/highlight.tsx +3 -0
- package/libs/ui/src/components/ui/hover-card.tsx +36 -0
- package/libs/ui/src/components/ui/inline-citation.tsx +251 -0
- package/libs/ui/src/components/ui/input-group.tsx +156 -0
- package/libs/ui/src/components/ui/input-otp.tsx +78 -0
- package/libs/ui/src/components/ui/input.tsx +21 -0
- package/libs/ui/src/components/ui/label.tsx +19 -0
- package/libs/ui/src/components/ui/model-selector.tsx +174 -0
- package/libs/ui/src/components/ui/multisidebar.tsx +750 -0
- package/libs/ui/src/components/ui/popover.tsx +43 -0
- package/libs/ui/src/components/ui/progress.tsx +28 -0
- package/libs/ui/src/components/ui/reasoning.tsx +178 -0
- package/libs/ui/src/components/ui/resizable.tsx +49 -0
- package/libs/ui/src/components/ui/scroll-area.tsx +54 -0
- package/libs/ui/src/components/ui/select.tsx +171 -0
- package/libs/ui/src/components/ui/separator.tsx +26 -0
- package/libs/ui/src/components/ui/sheet.tsx +128 -0
- package/libs/ui/src/components/ui/shimmer.tsx +53 -0
- package/libs/ui/src/components/ui/skeleton.tsx +13 -0
- package/libs/ui/src/components/ui/sonner.tsx +23 -0
- package/libs/ui/src/components/ui/switch.tsx +26 -0
- package/libs/ui/src/components/ui/table.tsx +96 -0
- package/libs/ui/src/components/ui/tabs.tsx +52 -0
- package/libs/ui/src/components/ui/textarea.tsx +41 -0
- package/libs/ui/src/components/ui/tool.tsx +209 -0
- package/libs/ui/src/components/ui/tooltip.tsx +58 -0
- package/libs/ui/src/components/ui/typography.tsx +113 -0
- package/libs/ui/src/fonts/manrope-v15-latin-300.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-400.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-500.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-600.woff2 +0 -0
- package/libs/ui/src/hooks/use-mobile.ts +19 -0
- package/libs/ui/src/lib/utils.ts +6 -0
- package/libs/ui/src/styles/fonts.css +35 -0
- package/libs/ui/src/styles/style.css +218 -0
- package/libs/ui/tsconfig.json +21 -0
- package/libs/ui/vite.config.ts +80 -0
- package/libs/ui-lit/README.md +245 -0
- package/libs/ui-lit/TESTING_GUIDE.md +296 -0
- package/libs/ui-lit/eslint.config.mjs +3 -0
- package/libs/ui-lit/package.json +41 -0
- package/libs/ui-lit/scripts/build-css.js +43 -0
- package/libs/ui-lit/src/components/sea-alert.ts +132 -0
- package/libs/ui-lit/src/components/sea-button.ts +95 -0
- package/libs/ui-lit/src/components/sea-card.ts +113 -0
- package/libs/ui-lit/src/components/sea-input.ts +184 -0
- package/libs/ui-lit/src/components/sea-spinner.ts +65 -0
- package/libs/ui-lit/src/index.ts +15 -0
- package/libs/ui-lit/src/lib/utils.ts +6 -0
- package/libs/ui-lit/src/styles/tailwind.css +76 -0
- package/libs/ui-lit/src/theme.css +66 -0
- package/libs/ui-lit/src/theme.ts +79 -0
- package/libs/ui-lit/src/vite-env.d.ts +6 -0
- package/libs/ui-lit/tailwind.config.ts +50 -0
- package/libs/ui-lit/test.html +289 -0
- package/libs/ui-lit/tsconfig.json +23 -0
- package/libs/ui-lit/vite.config.ts +31 -0
- package/libs/ui-lit/vite.css.config.ts +20 -0
- package/libs/util/.prettierignore +6 -0
- package/libs/util/.prettierrc.js +12 -0
- package/libs/util/eslint.config.mjs +3 -0
- package/libs/util/package.json +45 -0
- package/libs/util/src/billing.ts +10 -0
- package/libs/util/src/data-transform.ts +19 -0
- package/libs/util/src/encryption.ts +45 -0
- package/libs/util/src/fmt.test.ts +9 -0
- package/libs/util/src/fmt.ts +71 -0
- package/libs/util/src/fuzzy.ts +47 -0
- package/libs/util/src/id.ts +24 -0
- package/libs/util/src/invariant.ts +31 -0
- package/libs/util/src/sub-name.ts +7 -0
- package/libs/util/tsconfig.json +19 -0
- package/libs/util/vite.config.ts +34 -0
- package/package.json +28 -0
- package/packages/widget/.prettierignore +6 -0
- package/packages/widget/.prettierrc.js +12 -0
- package/packages/widget/README.md +95 -0
- package/packages/widget/eslint.config.mjs +11 -0
- package/packages/widget/openapi-ts.config.ts +8 -0
- package/packages/widget/package.json +89 -0
- package/packages/widget/postcss.config.mjs +10 -0
- package/packages/widget/src/clients/api/client/client.ts +187 -0
- package/packages/widget/src/clients/api/client/index.ts +22 -0
- package/packages/widget/src/clients/api/client/types.ts +192 -0
- package/packages/widget/src/clients/api/client/utils.ts +394 -0
- package/packages/widget/src/clients/api/client.gen.ts +18 -0
- package/packages/widget/src/clients/api/core/auth.ts +39 -0
- package/packages/widget/src/clients/api/core/bodySerializer.ts +74 -0
- package/packages/widget/src/clients/api/core/params.ts +132 -0
- package/packages/widget/src/clients/api/core/pathSerializer.ts +169 -0
- package/packages/widget/src/clients/api/core/types.ts +80 -0
- package/packages/widget/src/clients/api/index.ts +3 -0
- package/packages/widget/src/clients/api/sdk.gen.ts +805 -0
- package/packages/widget/src/clients/api/types.gen.ts +2085 -0
- package/packages/widget/src/components/container.tsx +42 -0
- package/packages/widget/src/components/data-display.tsx +384 -0
- package/packages/widget/src/components/data-viewer.tsx +311 -0
- package/packages/widget/src/components/doc-list.tsx +102 -0
- package/packages/widget/src/components/field-correction-modal.tsx +265 -0
- package/packages/widget/src/components/header.tsx +71 -0
- package/packages/widget/src/components/new-submission.tsx +290 -0
- package/packages/widget/src/components/sidebar-right.tsx +19 -0
- package/packages/widget/src/components/submission-card.tsx +66 -0
- package/packages/widget/src/components/submission-page.tsx +75 -0
- package/packages/widget/src/components/upload-doc.tsx +241 -0
- package/packages/widget/src/components/widget.tsx +101 -0
- package/packages/widget/src/index.tsx +167 -0
- package/packages/widget/src/lib/config.ts +2 -0
- package/packages/widget/src/lib/util.ts +40 -0
- package/packages/widget/src/styles/index.css +5 -0
- package/packages/widget/src/styles/tw-properties.css +337 -0
- package/packages/widget/src/vite-env.d.ts +3 -0
- package/packages/widget/tsconfig.app.json +35 -0
- package/packages/widget/tsconfig.json +4 -0
- package/packages/widget/tsconfig.node.json +24 -0
- package/packages/widget/vite.config.ts +116 -0
- package/packages/widget-lit/BOTTLENECKS.md +250 -0
- package/packages/widget-lit/IMPLEMENTATION_SUMMARY.md +295 -0
- package/packages/widget-lit/README.md +232 -0
- package/packages/widget-lit/eslint.config.mjs +3 -0
- package/packages/widget-lit/package.json +52 -0
- package/packages/widget-lit/src/api-client.ts +230 -0
- package/packages/widget-lit/src/api-client.ts.backup +218 -0
- package/packages/widget-lit/src/components/sea-chat.ts +382 -0
- package/packages/widget-lit/src/components/sea-submission-viewer.ts +267 -0
- package/packages/widget-lit/src/components/sea-widget.ts +317 -0
- package/packages/widget-lit/src/index.ts +48 -0
- package/packages/widget-lit/src/react.ts +58 -0
- package/packages/widget-lit/src/style.css +47 -0
- package/packages/widget-lit/tsconfig.json +24 -0
- package/packages/widget-lit/vite.config.ts +29 -0
- package/packages/widget-ng/DEVELOPMENT.md +74 -0
- package/packages/widget-ng/README.md +657 -0
- package/packages/widget-ng/dev.sh +14 -0
- package/packages/widget-ng/eslint.config.mjs +24 -0
- package/packages/widget-ng/ng-package.json +9 -0
- package/packages/widget-ng/package.json +85 -0
- package/packages/widget-ng/src/index.ts +45 -0
- package/packages/widget-ng/src/lib/components/sea-chat.component.ts +737 -0
- package/packages/widget-ng/src/lib/components/sea-data-viewer.component.ts +2240 -0
- package/packages/widget-ng/src/lib/components/sea-deal-form-modal.component.ts +702 -0
- package/packages/widget-ng/src/lib/components/sea-document-list.component.ts +350 -0
- package/packages/widget-ng/src/lib/components/sea-feedback-modal.component.ts +461 -0
- package/packages/widget-ng/src/lib/components/sea-file-upload.component.ts +655 -0
- package/packages/widget-ng/src/lib/components/sea-model-selection-modal.component.ts +367 -0
- package/packages/widget-ng/src/lib/components/sea-new-submission-modal.component.ts +414 -0
- package/packages/widget-ng/src/lib/components/sea-pdf-viewer.component.ts +869 -0
- package/packages/widget-ng/src/lib/components/sea-submission-card.component.ts +251 -0
- package/packages/widget-ng/src/lib/components/sea-widget.component.ts +684 -0
- package/packages/widget-ng/src/lib/models/submission.model.ts +170 -0
- package/packages/widget-ng/src/lib/pipes/markdown.pipe.ts +57 -0
- package/packages/widget-ng/src/lib/services/api-client.service.ts +715 -0
- package/packages/widget-ng/src/lib/services/chat.service.ts +330 -0
- package/packages/widget-ng/src/lib/services/config.service.ts +107 -0
- package/packages/widget-ng/src/web-component.ts +56 -0
- package/packages/widget-ng/tsconfig.json +25 -0
- package/packages/widget-ng/tsconfig.lib.json +9 -0
- package/packages/widget-ng/vite.config.elements.ts +26 -0
- package/packages/widget-ng/vitest.config.ts +19 -0
- package/packages/widget-ng/vitest.setup.ts +13 -0
- package/pnpm-workspace.yaml +18 -0
- package/render.yaml +136 -0
- package/scripts/README.md +57 -0
- package/scripts/package.json +22 -0
- package/scripts/python/.python-version +1 -0
- package/scripts/python/README.md +3 -0
- package/scripts/python/export-org-data.py +693 -0
- package/scripts/python/pyproject.toml +29 -0
- package/scripts/python/requirements-dev.lock +36 -0
- package/scripts/python/requirements.lock +36 -0
- package/scripts/python/src/gen.py +297 -0
- package/scripts/python/test.py +34 -0
- package/scripts/src/fix-storage-provider-mismatch.ts +239 -0
- package/scripts/src/sync-render-yaml.ts +290 -0
- package/scripts/src/test-chat-stream.ts +300 -0
- package/scripts/src/test-reconciliation.ts +230 -0
- package/scripts/tsconfig.json +15 -0
- package/tests/angular-test-app/.vscode/extensions.json +4 -0
- package/tests/angular-test-app/.vscode/launch.json +13 -0
- package/tests/angular-test-app/.vscode/tasks.json +24 -0
- package/tests/angular-test-app/README.md +59 -0
- package/tests/angular-test-app/angular.json +111 -0
- package/tests/angular-test-app/clean-start.sh +14 -0
- package/tests/angular-test-app/package.json +36 -0
- package/tests/angular-test-app/public/favicon.ico +0 -0
- package/tests/angular-test-app/src/app/app.component.ts +220 -0
- package/tests/angular-test-app/src/app/app.config.ts +5 -0
- package/tests/angular-test-app/src/env.d.ts +13 -0
- package/tests/angular-test-app/src/index.html +13 -0
- package/tests/angular-test-app/src/main.ts +6 -0
- package/tests/angular-test-app/src/styles.css +8 -0
- package/tests/angular-test-app/tsconfig.app.json +15 -0
- package/tests/angular-test-app/tsconfig.json +27 -0
- package/tests/crm-viewer-app/API_INTEGRATION_SUMMARY.md +295 -0
- package/tests/crm-viewer-app/CURRENT_ASSETS_FIELDS.md +148 -0
- package/tests/crm-viewer-app/FIELD_ID_MAPPING.md +206 -0
- package/tests/crm-viewer-app/INTEGRATION_GUIDE.md +309 -0
- package/tests/crm-viewer-app/README.md +174 -0
- package/tests/crm-viewer-app/REAL_API_INTEGRATION.md +240 -0
- package/tests/crm-viewer-app/UPDATED_IMPLEMENTATION.md +279 -0
- package/tests/crm-viewer-app/angular.json +114 -0
- package/tests/crm-viewer-app/package.json +35 -0
- package/tests/crm-viewer-app/src/app/app.component.ts +534 -0
- package/tests/crm-viewer-app/src/app/citation.service.ts +316 -0
- package/tests/crm-viewer-app/src/env.d.ts +16 -0
- package/tests/crm-viewer-app/src/index.html +19 -0
- package/tests/crm-viewer-app/src/main.ts +7 -0
- package/tests/crm-viewer-app/src/styles.css +409 -0
- package/tests/crm-viewer-app/src/template.html +2678 -0
- package/tests/crm-viewer-app/tsconfig.app.json +15 -0
- package/tests/crm-viewer-app/tsconfig.json +27 -0
- package/tests/e2e/package.json +17 -0
- package/tests/e2e/playwright.config.ts +75 -0
- package/tests/e2e/tests/api/health.spec.ts +10 -0
- package/tests/e2e/tests/app/example.spec.ts +10 -0
- package/tests/widget-test-app/.prettierignore +6 -0
- package/tests/widget-test-app/README.md +48 -0
- package/tests/widget-test-app/index.html +12 -0
- package/tests/widget-test-app/package.json +24 -0
- package/tests/widget-test-app/src/App.css +192 -0
- package/tests/widget-test-app/src/App.tsx +80 -0
- package/tests/widget-test-app/src/main.tsx +9 -0
- package/tests/widget-test-app/src/vite-env.d.ts +4 -0
- package/tests/widget-test-app/tsconfig.json +25 -0
- package/tests/widget-test-app/tsconfig.node.json +11 -0
- package/tests/widget-test-app/vite.config.ts +14 -0
|
@@ -0,0 +1,2342 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPipelineRun,
|
|
3
|
+
createPipelineRunStep,
|
|
4
|
+
getPipelineWithSteps,
|
|
5
|
+
getPipelineStepsWithLevels,
|
|
6
|
+
getParentStepRuns,
|
|
7
|
+
getParentStepRunsWithDependencies,
|
|
8
|
+
getRuntimeSubmission,
|
|
9
|
+
getRuntimeSubmissionWithData,
|
|
10
|
+
storePipelineOutputData,
|
|
11
|
+
updatePipelineRunStatus,
|
|
12
|
+
updatePipelineRunStepStatus,
|
|
13
|
+
logRequestCall,
|
|
14
|
+
getSubmissionGroups,
|
|
15
|
+
getGroupForStepRun,
|
|
16
|
+
resolveStepBlockersOnCompletion,
|
|
17
|
+
getPipelineStepById,
|
|
18
|
+
getStepDocuments,
|
|
19
|
+
getPipelineRunWithSteps,
|
|
20
|
+
getStepDependenciesByParentStepId,
|
|
21
|
+
getStepIntegrationConfig,
|
|
22
|
+
} from "@sea/dal/pipeline";
|
|
23
|
+
import { PIPELINE_RUN_STATUS, PIPELINE_RUN_STEP_STATUS } from "@sea/db";
|
|
24
|
+
import {
|
|
25
|
+
createSubmissionWithConversation,
|
|
26
|
+
getDenormSubmission,
|
|
27
|
+
syncSubmissionToForm,
|
|
28
|
+
getSubmissionWithData,
|
|
29
|
+
type DenormSubmission,
|
|
30
|
+
} from "@sea/dal/submission";
|
|
31
|
+
import type { FormGroupHydrated, Member, PipelineRunStep } from "@sea/db/types";
|
|
32
|
+
import { db, schema } from "@sea/db";
|
|
33
|
+
import { and, asc, eq, inArray, or } from "drizzle-orm";
|
|
34
|
+
import type {
|
|
35
|
+
ExecutePipelineInput,
|
|
36
|
+
ExecutePipelineResult,
|
|
37
|
+
StepExecutionResult,
|
|
38
|
+
} from "@sea/schemas/core/pipeline";
|
|
39
|
+
import {
|
|
40
|
+
addAdditionalPropertiesToJsonSchema,
|
|
41
|
+
buildExtractionSchemaFromGroup,
|
|
42
|
+
} from "@sea/core/data-extraction/structuring/custom";
|
|
43
|
+
import {
|
|
44
|
+
jsonSchema,
|
|
45
|
+
Output,
|
|
46
|
+
stepCountIs,
|
|
47
|
+
ToolLoopAgent,
|
|
48
|
+
zodSchema,
|
|
49
|
+
type JSONSchema7,
|
|
50
|
+
} from "ai";
|
|
51
|
+
import * as z from "zod/v4";
|
|
52
|
+
import type { createLanguageModel } from "@sea/ai/models";
|
|
53
|
+
import type { DalResult } from "@sea/dal/result";
|
|
54
|
+
import { HatchetClientMgr } from "@sea/clients/hatchet";
|
|
55
|
+
import type { HatchetConfig } from "@sea/config";
|
|
56
|
+
import { createPipelineArtifact } from "../chat/tools/pipeline-artifact";
|
|
57
|
+
import pLimit from "p-limit";
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Serialization helpers
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
function serializeSubmission(submission: DenormSubmission): string {
|
|
64
|
+
const htmlParts: string[] = [];
|
|
65
|
+
|
|
66
|
+
// Add submission header
|
|
67
|
+
htmlParts.push(`<h2>${submission.formName}</h2>`);
|
|
68
|
+
if (submission.name) {
|
|
69
|
+
htmlParts.push(`<p><strong>Submission:</strong> ${submission.name}</p>`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Serialize single groups (definition lists)
|
|
73
|
+
for (const group of submission.groups) {
|
|
74
|
+
htmlParts.push(`<h3>${group.name}</h3>`);
|
|
75
|
+
htmlParts.push("<dl>");
|
|
76
|
+
|
|
77
|
+
for (const item of group.items) {
|
|
78
|
+
htmlParts.push(`<dt>${item.name}</dt>`);
|
|
79
|
+
htmlParts.push(`<dd id="${item.id}">${String(item.value ?? "")}</dd>`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Add derived fields if any
|
|
83
|
+
for (const derived of group.derived) {
|
|
84
|
+
htmlParts.push(`<dt>${derived.name} (derived)</dt>`);
|
|
85
|
+
htmlParts.push(
|
|
86
|
+
`<dd id="${derived.id}">${String(derived.value ?? "")}</dd>`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
htmlParts.push("</dl>");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Serialize tables (multi groups)
|
|
94
|
+
for (const table of submission.tables) {
|
|
95
|
+
htmlParts.push(`<h3>${table.name}</h3>`);
|
|
96
|
+
htmlParts.push("<table>");
|
|
97
|
+
|
|
98
|
+
// Table header
|
|
99
|
+
htmlParts.push("<thead><tr>");
|
|
100
|
+
for (const col of table.columns) {
|
|
101
|
+
htmlParts.push(`<th>${col}</th>`);
|
|
102
|
+
}
|
|
103
|
+
htmlParts.push("</tr></thead>");
|
|
104
|
+
|
|
105
|
+
// Table body
|
|
106
|
+
htmlParts.push("<tbody>");
|
|
107
|
+
for (const row of table.rows) {
|
|
108
|
+
htmlParts.push("<tr>");
|
|
109
|
+
|
|
110
|
+
// Regular items
|
|
111
|
+
for (const item of row.items) {
|
|
112
|
+
htmlParts.push(`<td id="${item.id}">${String(item.value ?? "")}</td>`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Derived items
|
|
116
|
+
for (const derived of row.derived) {
|
|
117
|
+
htmlParts.push(
|
|
118
|
+
`<td id="${derived.id}">${String(derived.value ?? "")}</td>`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
htmlParts.push("</tr>");
|
|
123
|
+
}
|
|
124
|
+
htmlParts.push("</tbody>");
|
|
125
|
+
htmlParts.push("</table>");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return htmlParts.join("\n");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function serializePipelineResources(resources: DenormSubmission[]): string {
|
|
132
|
+
const htmlParts: string[] = [];
|
|
133
|
+
|
|
134
|
+
for (const resource of resources) {
|
|
135
|
+
htmlParts.push(serializeSubmission(resource));
|
|
136
|
+
htmlParts.push("<hr>");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return htmlParts.join("\n");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================================================
|
|
143
|
+
// DAG Execution Engine (Phase 1)
|
|
144
|
+
// ============================================================================
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Execute pipeline using DAG traversal with level-by-level execution.
|
|
148
|
+
* This replaces the old sequential execution approach.
|
|
149
|
+
*/
|
|
150
|
+
export async function executePipelineDAG({
|
|
151
|
+
member,
|
|
152
|
+
runId,
|
|
153
|
+
config,
|
|
154
|
+
}: {
|
|
155
|
+
member: Member;
|
|
156
|
+
runId: string;
|
|
157
|
+
config: {
|
|
158
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
159
|
+
hatchet?: HatchetConfig;
|
|
160
|
+
};
|
|
161
|
+
}): Promise<ExecutePipelineResult> {
|
|
162
|
+
try {
|
|
163
|
+
// Get pipeline run via DAL
|
|
164
|
+
const runResult = await getPipelineRunWithSteps({ member, runId });
|
|
165
|
+
if (!runResult.ok) {
|
|
166
|
+
return { ok: false, code: 404, error: "Pipeline run not found" };
|
|
167
|
+
}
|
|
168
|
+
const run = runResult.data;
|
|
169
|
+
|
|
170
|
+
// Update run status to running
|
|
171
|
+
await updatePipelineRunStatus({
|
|
172
|
+
member,
|
|
173
|
+
runId,
|
|
174
|
+
status: PIPELINE_RUN_STEP_STATUS.RUNNING,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Get execution levels for topological ordering
|
|
178
|
+
const levelsResult = await getPipelineStepsWithLevels({
|
|
179
|
+
member,
|
|
180
|
+
pipelineId: run.pipelineId,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
if (!levelsResult.ok) {
|
|
184
|
+
await updatePipelineRunStatus({
|
|
185
|
+
member,
|
|
186
|
+
runId,
|
|
187
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
188
|
+
errorSummary: "Failed to compute execution levels",
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
code: 500,
|
|
193
|
+
error: "Failed to compute execution levels",
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const stepsWithLevels = levelsResult.data;
|
|
198
|
+
|
|
199
|
+
// Group steps by execution level
|
|
200
|
+
const levelMap = new Map<number, typeof stepsWithLevels>();
|
|
201
|
+
for (const step of stepsWithLevels) {
|
|
202
|
+
const level = step.executionLevel;
|
|
203
|
+
if (!levelMap.has(level)) {
|
|
204
|
+
levelMap.set(level, []);
|
|
205
|
+
}
|
|
206
|
+
levelMap.get(level)!.push(step);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const levels = Array.from(levelMap.keys()).sort((a, b) => a - b);
|
|
210
|
+
|
|
211
|
+
// Execute level by level
|
|
212
|
+
const limit = pLimit(5); // Concurrent execution limit per level
|
|
213
|
+
console.log(
|
|
214
|
+
"[executePipelineDAG] Executing levels:",
|
|
215
|
+
JSON.stringify(levels, null, 2),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
for (const level of levels) {
|
|
219
|
+
const stepsInLevel = levelMap.get(level) || [];
|
|
220
|
+
console.log(
|
|
221
|
+
`[executePipelineDAG] Executing level ${level} with ${stepsInLevel.length} steps`,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Execute all steps in this level concurrently (with limit)
|
|
225
|
+
const levelPromises = stepsInLevel.map((step) =>
|
|
226
|
+
limit(() =>
|
|
227
|
+
executeStepByRole({
|
|
228
|
+
member,
|
|
229
|
+
runId,
|
|
230
|
+
stepId: step.id,
|
|
231
|
+
config,
|
|
232
|
+
}),
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// Wait for all steps in this level to complete (or be blocked/failed)
|
|
238
|
+
await Promise.all(levelPromises);
|
|
239
|
+
|
|
240
|
+
// After level completes, check if any steps are still blocked
|
|
241
|
+
// If so, we cannot proceed to the next level
|
|
242
|
+
const levelStepIds = stepsInLevel.map((s) => s.id);
|
|
243
|
+
const runSteps = await db.query.pipelineRunStep.findMany({
|
|
244
|
+
where: and(
|
|
245
|
+
eq(schema.pipelineRunStep.runId, runId),
|
|
246
|
+
// Filter to steps in this level
|
|
247
|
+
or(
|
|
248
|
+
...levelStepIds.map((id) =>
|
|
249
|
+
eq(schema.pipelineRunStep.stepId, id),
|
|
250
|
+
),
|
|
251
|
+
),
|
|
252
|
+
),
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const blockedSteps = runSteps.filter(
|
|
256
|
+
(rs) => rs.status === PIPELINE_RUN_STEP_STATUS.BLOCKED,
|
|
257
|
+
);
|
|
258
|
+
const failedSteps = runSteps.filter(
|
|
259
|
+
(rs) => rs.status === PIPELINE_RUN_STEP_STATUS.FAILED,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
if (failedSteps.length > 0) {
|
|
263
|
+
console.error(
|
|
264
|
+
`[executePipelineDAG] Level ${level} has ${failedSteps.length} failed step(s)`,
|
|
265
|
+
);
|
|
266
|
+
await updatePipelineRunStatus({
|
|
267
|
+
member,
|
|
268
|
+
runId,
|
|
269
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
270
|
+
errorSummary: `${failedSteps.length} step(s) failed in level ${level}`,
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
ok: false,
|
|
274
|
+
code: 500,
|
|
275
|
+
error: `Level ${level} had failed steps`,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (blockedSteps.length > 0) {
|
|
280
|
+
console.log(
|
|
281
|
+
`[executePipelineDAG] Level ${level} has ${blockedSteps.length} blocked step(s), marking run as blocked`,
|
|
282
|
+
);
|
|
283
|
+
await updatePipelineRunStatus({
|
|
284
|
+
member,
|
|
285
|
+
runId,
|
|
286
|
+
status: PIPELINE_RUN_STATUS.BLOCKED,
|
|
287
|
+
});
|
|
288
|
+
return {
|
|
289
|
+
ok: false,
|
|
290
|
+
code: 400,
|
|
291
|
+
error: `${blockedSteps.length} step(s) are blocked - resolve blockers to continue`,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// After level completes, check for spawned instances and execute them
|
|
296
|
+
console.log(
|
|
297
|
+
`[executePipelineDAG] Checking for spawned instances after level ${level}`,
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// First check all run steps to see what's there
|
|
301
|
+
const allRunSteps = await db.query.pipelineRunStep.findMany({
|
|
302
|
+
where: eq(schema.pipelineRunStep.runId, runId),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
console.log(
|
|
306
|
+
`[executePipelineDAG] All run steps (${allRunSteps.length}):`,
|
|
307
|
+
allRunSteps.map((rs) => ({
|
|
308
|
+
id: rs.id,
|
|
309
|
+
stepId: rs.stepId,
|
|
310
|
+
spawnType: rs.spawnType,
|
|
311
|
+
status: rs.status,
|
|
312
|
+
stepRunIndex: rs.stepRunIndex,
|
|
313
|
+
})),
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const spawnedInstances = await db.query.pipelineRunStep.findMany({
|
|
317
|
+
where: and(
|
|
318
|
+
eq(schema.pipelineRunStep.runId, runId),
|
|
319
|
+
eq(schema.pipelineRunStep.spawnType, "instance"),
|
|
320
|
+
eq(schema.pipelineRunStep.status, "pending"),
|
|
321
|
+
),
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
console.log(
|
|
325
|
+
`[executePipelineDAG] Spawned instances query result (${spawnedInstances.length}):`,
|
|
326
|
+
spawnedInstances.map((si) => ({
|
|
327
|
+
id: si.id,
|
|
328
|
+
stepId: si.stepId,
|
|
329
|
+
spawnType: si.spawnType,
|
|
330
|
+
status: si.status,
|
|
331
|
+
stepRunIndex: si.stepRunIndex,
|
|
332
|
+
})),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (spawnedInstances.length > 0) {
|
|
336
|
+
console.log(
|
|
337
|
+
`[executePipelineDAG] Found ${spawnedInstances.length} spawned instance(s), executing in parallel (max 10)`,
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Execute spawned instances with max 10 concurrency
|
|
341
|
+
const instanceLimit = pLimit(10);
|
|
342
|
+
const instancePromises = spawnedInstances.map((instance) =>
|
|
343
|
+
instanceLimit(() =>
|
|
344
|
+
executeInstanceStep({
|
|
345
|
+
member,
|
|
346
|
+
runId,
|
|
347
|
+
runStepId: instance.id,
|
|
348
|
+
stepId: instance.stepId!,
|
|
349
|
+
config,
|
|
350
|
+
}),
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
await Promise.all(instancePromises);
|
|
355
|
+
|
|
356
|
+
// Check if any instances failed or are blocked
|
|
357
|
+
const completedInstances = await db.query.pipelineRunStep.findMany({
|
|
358
|
+
where: and(
|
|
359
|
+
eq(schema.pipelineRunStep.runId, runId),
|
|
360
|
+
inArray(
|
|
361
|
+
schema.pipelineRunStep.id,
|
|
362
|
+
spawnedInstances.map((i) => i.id),
|
|
363
|
+
),
|
|
364
|
+
),
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const failedInstances = completedInstances.filter(
|
|
368
|
+
(i) => i.status === PIPELINE_RUN_STEP_STATUS.FAILED,
|
|
369
|
+
);
|
|
370
|
+
const blockedInstances = completedInstances.filter(
|
|
371
|
+
(i) => i.status === PIPELINE_RUN_STEP_STATUS.BLOCKED,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
if (failedInstances.length > 0) {
|
|
375
|
+
console.error(
|
|
376
|
+
`[executePipelineDAG] ${failedInstances.length} spawned instance(s) failed`,
|
|
377
|
+
);
|
|
378
|
+
await updatePipelineRunStatus({
|
|
379
|
+
member,
|
|
380
|
+
runId,
|
|
381
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
382
|
+
errorSummary: `${failedInstances.length} spawned instance(s) failed in level ${level}`,
|
|
383
|
+
});
|
|
384
|
+
return {
|
|
385
|
+
ok: false,
|
|
386
|
+
code: 500,
|
|
387
|
+
error: `Spawned instances failed in level ${level}`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (blockedInstances.length > 0) {
|
|
392
|
+
console.log(
|
|
393
|
+
`[executePipelineDAG] ${blockedInstances.length} spawned instance(s) blocked`,
|
|
394
|
+
);
|
|
395
|
+
await updatePipelineRunStatus({
|
|
396
|
+
member,
|
|
397
|
+
runId,
|
|
398
|
+
status: PIPELINE_RUN_STATUS.BLOCKED,
|
|
399
|
+
});
|
|
400
|
+
return {
|
|
401
|
+
ok: false,
|
|
402
|
+
code: 400,
|
|
403
|
+
error: `${blockedInstances.length} spawned instance(s) are blocked`,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
console.log(
|
|
408
|
+
`[executePipelineDAG] All ${spawnedInstances.length} spawned instance(s) completed successfully`,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
console.log(
|
|
413
|
+
`[executePipelineDAG] Level ${level} completed successfully`,
|
|
414
|
+
);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error(`[executePipelineDAG] Level ${level} failed:`, error);
|
|
417
|
+
await updatePipelineRunStatus({
|
|
418
|
+
member,
|
|
419
|
+
runId,
|
|
420
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
421
|
+
errorSummary:
|
|
422
|
+
error instanceof Error ? error.message : "Level execution failed",
|
|
423
|
+
});
|
|
424
|
+
return {
|
|
425
|
+
ok: false,
|
|
426
|
+
code: 500,
|
|
427
|
+
error: `Level ${level} execution failed`,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Update run status to completed
|
|
433
|
+
await updatePipelineRunStatus({
|
|
434
|
+
member,
|
|
435
|
+
runId,
|
|
436
|
+
status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
ok: true,
|
|
441
|
+
code: 200,
|
|
442
|
+
data: { runId, status: PIPELINE_RUN_STATUS.COMPLETED },
|
|
443
|
+
};
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error("[executePipelineDAG] Error:", error);
|
|
446
|
+
await updatePipelineRunStatus({
|
|
447
|
+
member,
|
|
448
|
+
runId,
|
|
449
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
450
|
+
errorSummary: error instanceof Error ? error.message : "Unknown error",
|
|
451
|
+
});
|
|
452
|
+
return {
|
|
453
|
+
ok: false,
|
|
454
|
+
code: 500,
|
|
455
|
+
error: "Pipeline execution failed",
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Dispatcher that routes step execution based on role.
|
|
462
|
+
* Handles backwards compatibility for steps without a role.
|
|
463
|
+
*/
|
|
464
|
+
async function executeStepByRole({
|
|
465
|
+
member,
|
|
466
|
+
runId,
|
|
467
|
+
stepId,
|
|
468
|
+
config,
|
|
469
|
+
}: {
|
|
470
|
+
member: Member;
|
|
471
|
+
runId: string;
|
|
472
|
+
stepId: string;
|
|
473
|
+
config: {
|
|
474
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
475
|
+
hatchet?: HatchetConfig;
|
|
476
|
+
};
|
|
477
|
+
}): Promise<void> {
|
|
478
|
+
// Get step definition
|
|
479
|
+
const stepResult = await getPipelineStepById({ member, stepId });
|
|
480
|
+
|
|
481
|
+
if (!stepResult.ok) {
|
|
482
|
+
throw new Error(`Step ${stepId} not found`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const step = stepResult.data;
|
|
486
|
+
|
|
487
|
+
// Get the existing run step (created when the run was initialized)
|
|
488
|
+
const existingRunStep = await db.query.pipelineRunStep.findFirst({
|
|
489
|
+
where: and(
|
|
490
|
+
eq(schema.pipelineRunStep.runId, runId),
|
|
491
|
+
eq(schema.pipelineRunStep.stepId, stepId),
|
|
492
|
+
or(
|
|
493
|
+
eq(schema.pipelineRunStep.spawnType, "normal"),
|
|
494
|
+
eq(schema.pipelineRunStep.spawnType, "template"),
|
|
495
|
+
),
|
|
496
|
+
),
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (!existingRunStep) {
|
|
500
|
+
throw new Error(`Run step not found for step ${stepId}`);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// If it's a template step, check if we should spawn instances now
|
|
504
|
+
if (existingRunStep.spawnType === "template") {
|
|
505
|
+
console.log(
|
|
506
|
+
`[executeStepByRole] Template step ${stepId} encountered - checking if ready to spawn instances`,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
// Check if parent dependencies are satisfied
|
|
510
|
+
const parentStepRunsResult = await getParentStepRuns({
|
|
511
|
+
member,
|
|
512
|
+
runStepId: existingRunStep.id,
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
if (!parentStepRunsResult.ok) {
|
|
516
|
+
console.error(
|
|
517
|
+
`[executeStepByRole] Failed to get parent steps for template ${stepId}`,
|
|
518
|
+
);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const parentStepRuns = parentStepRunsResult.data;
|
|
523
|
+
const incompleteParents = parentStepRuns.filter(
|
|
524
|
+
(p) => p.status !== PIPELINE_RUN_STEP_STATUS.COMPLETED,
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
if (incompleteParents.length > 0) {
|
|
528
|
+
console.log(
|
|
529
|
+
`[executeStepByRole] Template ${stepId} not ready - ${incompleteParents.length} parent(s) not completed`,
|
|
530
|
+
);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// All parents completed - try to spawn instances
|
|
535
|
+
console.log(
|
|
536
|
+
`[executeStepByRole] All parents completed for template ${stepId} - attempting to spawn instances`,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
await spawnInstancesForTemplate({
|
|
540
|
+
member,
|
|
541
|
+
runId,
|
|
542
|
+
runStep: existingRunStep,
|
|
543
|
+
step,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// Mark template as spawned
|
|
547
|
+
await updatePipelineRunStepStatus({
|
|
548
|
+
member,
|
|
549
|
+
runStepId: existingRunStep.id,
|
|
550
|
+
status: "spawned",
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
console.log(`[executeStepByRole] Template ${stepId} marked as spawned`);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const runStep = existingRunStep;
|
|
558
|
+
|
|
559
|
+
// Check for unresolved blockers before executing
|
|
560
|
+
const blockers = await db.query.pipelineRunStepBlocker.findMany({
|
|
561
|
+
where: and(
|
|
562
|
+
eq(schema.pipelineRunStepBlocker.runStepId, runStep.id),
|
|
563
|
+
eq(schema.pipelineRunStepBlocker.resolved, false),
|
|
564
|
+
),
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (blockers.length > 0) {
|
|
568
|
+
console.log(
|
|
569
|
+
`[executeStepByRole] Step ${stepId} has ${blockers.length} unresolved blocker(s), marking as blocked`,
|
|
570
|
+
);
|
|
571
|
+
await updatePipelineRunStepStatus({
|
|
572
|
+
member,
|
|
573
|
+
runStepId: runStep.id,
|
|
574
|
+
status: PIPELINE_RUN_STEP_STATUS.BLOCKED,
|
|
575
|
+
});
|
|
576
|
+
// Don't throw - just skip this step for now
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
try {
|
|
581
|
+
// Dispatch based on role (null role = legacy analysis step)
|
|
582
|
+
switch (step.role) {
|
|
583
|
+
case "extraction":
|
|
584
|
+
await executeExtractionStep({ member, runStep, step, config });
|
|
585
|
+
break;
|
|
586
|
+
case "integration":
|
|
587
|
+
await executeIntegrationStep({ member, runStep, step, config });
|
|
588
|
+
break;
|
|
589
|
+
case "analysis":
|
|
590
|
+
await executeAnalysisStep({ member, runStep, step, config });
|
|
591
|
+
break;
|
|
592
|
+
case "manual":
|
|
593
|
+
await executeManualStep({ member, runStep, step });
|
|
594
|
+
break;
|
|
595
|
+
default:
|
|
596
|
+
throw new Error(`Unknown step role: ${step.role}`);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// After successful completion:
|
|
600
|
+
// 1. Check for multi-group spawning (do this BEFORE marking complete)
|
|
601
|
+
await checkAndSpawnMultiGroups({ member, runStep, step });
|
|
602
|
+
|
|
603
|
+
// 2. Resolve this step's blockers and cascade to children
|
|
604
|
+
// This will mark parent_incomplete blockers on child steps as resolved
|
|
605
|
+
await resolveStepBlockersOnCompletion({
|
|
606
|
+
member,
|
|
607
|
+
runStepId: runStep.id,
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// 3. Verify output submission was created (critical for downstream steps)
|
|
611
|
+
const outputMapping =
|
|
612
|
+
await db.query.pipelineRunStepOutputSubmission.findFirst({
|
|
613
|
+
where: eq(schema.pipelineRunStepOutputSubmission.runStepId, runStep.id),
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
if (
|
|
617
|
+
step.role !== "manual" &&
|
|
618
|
+
step.role !== "integration" &&
|
|
619
|
+
!outputMapping
|
|
620
|
+
) {
|
|
621
|
+
console.error(
|
|
622
|
+
`[executeStepByRole] Step ${stepId} completed but no output submission was created!`,
|
|
623
|
+
);
|
|
624
|
+
throw new Error("Step completed without creating output submission");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
console.log(
|
|
628
|
+
`[executeStepByRole] Step ${stepId} completed successfully with output submission: ${outputMapping?.submissionId ?? "N/A"}`,
|
|
629
|
+
);
|
|
630
|
+
} catch (error) {
|
|
631
|
+
console.error(`[executeStepByRole] Step ${stepId} failed:`, error);
|
|
632
|
+
throw error;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Execute a spawned instance step
|
|
638
|
+
* Similar to executeStepByRole but operates on a specific runStepId (instance)
|
|
639
|
+
*/
|
|
640
|
+
async function executeInstanceStep({
|
|
641
|
+
member,
|
|
642
|
+
runId,
|
|
643
|
+
runStepId,
|
|
644
|
+
stepId,
|
|
645
|
+
config,
|
|
646
|
+
}: {
|
|
647
|
+
member: Member;
|
|
648
|
+
runId: string;
|
|
649
|
+
runStepId: string;
|
|
650
|
+
stepId: string;
|
|
651
|
+
config: {
|
|
652
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
653
|
+
hatchet?: HatchetConfig;
|
|
654
|
+
};
|
|
655
|
+
}): Promise<void> {
|
|
656
|
+
// Get step definition
|
|
657
|
+
const stepResult = await getPipelineStepById({ member, stepId });
|
|
658
|
+
|
|
659
|
+
if (!stepResult.ok) {
|
|
660
|
+
throw new Error(`Step ${stepId} not found`);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const step = stepResult.data;
|
|
664
|
+
|
|
665
|
+
// Get the specific instance run step
|
|
666
|
+
const runStep = await db.query.pipelineRunStep.findFirst({
|
|
667
|
+
where: eq(schema.pipelineRunStep.id, runStepId),
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
if (!runStep) {
|
|
671
|
+
throw new Error(`Run step ${runStepId} not found`);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (runStep.spawnType !== "instance") {
|
|
675
|
+
throw new Error(`Run step ${runStepId} is not an instance`);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Check for unresolved blockers before executing
|
|
679
|
+
const blockers = await db.query.pipelineRunStepBlocker.findMany({
|
|
680
|
+
where: and(
|
|
681
|
+
eq(schema.pipelineRunStepBlocker.runStepId, runStep.id),
|
|
682
|
+
eq(schema.pipelineRunStepBlocker.resolved, false),
|
|
683
|
+
),
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
if (blockers.length > 0) {
|
|
687
|
+
console.log(
|
|
688
|
+
`[executeInstanceStep] Instance ${runStepId} has ${blockers.length} unresolved blocker(s), marking as blocked`,
|
|
689
|
+
);
|
|
690
|
+
await updatePipelineRunStepStatus({
|
|
691
|
+
member,
|
|
692
|
+
runStepId: runStep.id,
|
|
693
|
+
status: PIPELINE_RUN_STEP_STATUS.BLOCKED,
|
|
694
|
+
});
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
try {
|
|
699
|
+
// Only analysis steps can have instances
|
|
700
|
+
if (step.role !== "analysis" && step.role !== null) {
|
|
701
|
+
throw new Error(`Step role ${step.role} cannot have instances`);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
await executeAnalysisStep({ member, runStep, step, config });
|
|
705
|
+
|
|
706
|
+
// Resolve blockers after completion
|
|
707
|
+
await resolveStepBlockersOnCompletion({
|
|
708
|
+
member,
|
|
709
|
+
runStepId: runStep.id,
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
console.log(
|
|
713
|
+
`[executeInstanceStep] Instance ${runStepId} completed successfully`,
|
|
714
|
+
);
|
|
715
|
+
} catch (error) {
|
|
716
|
+
console.error(`[executeInstanceStep] Instance ${runStepId} failed:`, error);
|
|
717
|
+
throw error;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Execute an extraction step (document extraction)
|
|
723
|
+
* Triggers async Hatchet workflow for each document via pushDoc()
|
|
724
|
+
*/
|
|
725
|
+
async function executeExtractionStep({
|
|
726
|
+
member,
|
|
727
|
+
runStep,
|
|
728
|
+
step,
|
|
729
|
+
config,
|
|
730
|
+
}: {
|
|
731
|
+
member: Member;
|
|
732
|
+
runStep: PipelineRunStep;
|
|
733
|
+
step: typeof schema.pipelineStep.$inferSelect & {
|
|
734
|
+
outputForms: Array<{
|
|
735
|
+
id: string;
|
|
736
|
+
stepId: string;
|
|
737
|
+
formId: string;
|
|
738
|
+
createdAt: Date;
|
|
739
|
+
}>;
|
|
740
|
+
};
|
|
741
|
+
config: {
|
|
742
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
743
|
+
hatchet?: HatchetConfig;
|
|
744
|
+
};
|
|
745
|
+
}): Promise<void> {
|
|
746
|
+
try {
|
|
747
|
+
await updatePipelineRunStepStatus({
|
|
748
|
+
member,
|
|
749
|
+
runStepId: runStep.id,
|
|
750
|
+
status: PIPELINE_RUN_STEP_STATUS.RUNNING,
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// 1. Get documents linked to this step run
|
|
754
|
+
const documentsResult = await getStepDocuments({
|
|
755
|
+
member,
|
|
756
|
+
runStepId: runStep.id,
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
if (!documentsResult.ok) {
|
|
760
|
+
throw new Error("Failed to load step documents");
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (documentsResult.data.length === 0) {
|
|
764
|
+
throw new Error("No documents linked to extraction step");
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// 2. Get output form
|
|
768
|
+
const outputFormId = step.outputForms[0]?.formId;
|
|
769
|
+
if (!outputFormId) {
|
|
770
|
+
throw new Error("Extraction step missing output form");
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// 3. Get pipeline run to access dealId
|
|
774
|
+
const pipelineRun = await db.query.pipelineRun.findFirst({
|
|
775
|
+
where: eq(schema.pipelineRun.id, runStep.runId),
|
|
776
|
+
});
|
|
777
|
+
if (!pipelineRun) {
|
|
778
|
+
throw new Error("Pipeline run not found");
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// 4. Create output submission for extracted data
|
|
782
|
+
const outputSubmission = await createSubmissionWithConversation({
|
|
783
|
+
member,
|
|
784
|
+
data: {
|
|
785
|
+
formId: outputFormId,
|
|
786
|
+
dealId: pipelineRun.dealId,
|
|
787
|
+
name:
|
|
788
|
+
runStep.spawnType === "instance"
|
|
789
|
+
? `${step.name} - Instance ${runStep.stepRunIndex! + 1}`
|
|
790
|
+
: `${step.name} output`,
|
|
791
|
+
status: "draft",
|
|
792
|
+
type: "extraction",
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
await syncSubmissionToForm({
|
|
797
|
+
member,
|
|
798
|
+
submissionId: outputSubmission.id,
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
// 5. For each document, create documentExtraction and trigger Hatchet workflow
|
|
802
|
+
if (!config.hatchet) {
|
|
803
|
+
throw new Error("Hatchet config required for extraction steps");
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const hatchetClient = new HatchetClientMgr(config.hatchet);
|
|
807
|
+
const extractionIds: string[] = [];
|
|
808
|
+
|
|
809
|
+
for (const document of documentsResult.data) {
|
|
810
|
+
// Create documentExtraction record
|
|
811
|
+
const [documentExtraction] = await db
|
|
812
|
+
.insert(schema.documentExtraction)
|
|
813
|
+
.values({
|
|
814
|
+
documentId: document.id,
|
|
815
|
+
submissionId: outputSubmission.id,
|
|
816
|
+
status: "pending",
|
|
817
|
+
extractionModel: "general",
|
|
818
|
+
extractionTrigger: "api",
|
|
819
|
+
})
|
|
820
|
+
.returning({ id: schema.documentExtraction.id });
|
|
821
|
+
|
|
822
|
+
if (!documentExtraction) {
|
|
823
|
+
throw new Error(
|
|
824
|
+
`Failed to create documentExtraction for document ${document.id}`,
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
extractionIds.push(documentExtraction.id);
|
|
829
|
+
|
|
830
|
+
// Trigger Hatchet extraction workflow
|
|
831
|
+
await hatchetClient.pushDoc(
|
|
832
|
+
documentExtraction.id,
|
|
833
|
+
"general",
|
|
834
|
+
step.prompt || undefined,
|
|
835
|
+
"medium",
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
console.log(
|
|
839
|
+
`[executeExtractionStep] Triggered extraction for document: ${document.name} (extractionId: ${documentExtraction.id})`,
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// 6. Link output submission to run step
|
|
844
|
+
await db.insert(schema.pipelineRunStepOutputSubmission).values({
|
|
845
|
+
runStepId: runStep.id,
|
|
846
|
+
submissionId: outputSubmission.id,
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
console.log(
|
|
850
|
+
`[executeExtractionStep] Waiting for ${extractionIds.length} extraction(s) to complete...`,
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
// 7. Poll for extraction completion
|
|
854
|
+
const MAX_WAIT_TIME_MS = 30 * 60 * 1000; // 30 minutes
|
|
855
|
+
const POLL_INTERVAL_MS = 5000; // 5 seconds
|
|
856
|
+
const startTime = Date.now();
|
|
857
|
+
|
|
858
|
+
while (true) {
|
|
859
|
+
// Check if we've exceeded max wait time
|
|
860
|
+
if (Date.now() - startTime > MAX_WAIT_TIME_MS) {
|
|
861
|
+
throw new Error(
|
|
862
|
+
`Extraction timed out after ${MAX_WAIT_TIME_MS / 1000} seconds`,
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Check status of all extractions
|
|
867
|
+
const extractions = await db.query.documentExtraction.findMany({
|
|
868
|
+
where: inArray(schema.documentExtraction.id, extractionIds),
|
|
869
|
+
columns: { id: true, status: true },
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
const allDone = extractions.every((e) => e.status === "done");
|
|
873
|
+
const anyFailed = extractions.some((e) => e.status === "failed");
|
|
874
|
+
|
|
875
|
+
if (anyFailed) {
|
|
876
|
+
const failedIds = extractions
|
|
877
|
+
.filter((e) => e.status === "failed")
|
|
878
|
+
.map((e) => e.id);
|
|
879
|
+
throw new Error(`Extraction failed for: ${failedIds.join(", ")}`);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (allDone) {
|
|
883
|
+
console.log(
|
|
884
|
+
`[executeExtractionStep] All ${extractionIds.length} extraction(s) completed successfully`,
|
|
885
|
+
);
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Wait before next poll
|
|
890
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// 8. Mark step as completed
|
|
894
|
+
await updatePipelineRunStepStatus({
|
|
895
|
+
member,
|
|
896
|
+
runStepId: runStep.id,
|
|
897
|
+
status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
console.log(
|
|
901
|
+
`[executeExtractionStep] Completed extraction step with ${documentsResult.data.length} document(s), output submission: ${outputSubmission.id}`,
|
|
902
|
+
);
|
|
903
|
+
} catch (error) {
|
|
904
|
+
console.error("[executeExtractionStep] Error:", error);
|
|
905
|
+
await updatePipelineRunStepStatus({
|
|
906
|
+
member,
|
|
907
|
+
runStepId: runStep.id,
|
|
908
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
909
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
910
|
+
});
|
|
911
|
+
throw error;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Execute an integration step (API call)
|
|
917
|
+
*/
|
|
918
|
+
async function executeIntegrationStep({
|
|
919
|
+
member,
|
|
920
|
+
runStep,
|
|
921
|
+
step,
|
|
922
|
+
config,
|
|
923
|
+
}: {
|
|
924
|
+
member: Member;
|
|
925
|
+
runStep: PipelineRunStep;
|
|
926
|
+
step: typeof schema.pipelineStep.$inferSelect & {
|
|
927
|
+
outputForms: Array<{
|
|
928
|
+
id: string;
|
|
929
|
+
stepId: string;
|
|
930
|
+
formId: string;
|
|
931
|
+
createdAt: Date;
|
|
932
|
+
}>;
|
|
933
|
+
};
|
|
934
|
+
config: { model: ReturnType<typeof createLanguageModel> };
|
|
935
|
+
}): Promise<void> {
|
|
936
|
+
try {
|
|
937
|
+
await updatePipelineRunStepStatus({
|
|
938
|
+
member,
|
|
939
|
+
runStepId: runStep.id,
|
|
940
|
+
status: PIPELINE_RUN_STEP_STATUS.RUNNING,
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
// Get integration config from separate table
|
|
944
|
+
const integrationConfigResult = await getStepIntegrationConfig({
|
|
945
|
+
member,
|
|
946
|
+
stepId: step.id,
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
if (!integrationConfigResult.ok || !integrationConfigResult.data) {
|
|
950
|
+
throw new Error("Integration step missing config");
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const integrationConfig = integrationConfigResult.data;
|
|
954
|
+
|
|
955
|
+
// Get parent step outputs to build request
|
|
956
|
+
const parentStepRunsResult = await getParentStepRuns({
|
|
957
|
+
member,
|
|
958
|
+
runStepId: runStep.id,
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
if (!parentStepRunsResult.ok) {
|
|
962
|
+
throw new Error("Failed to load parent step outputs");
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const parentStepRuns = parentStepRunsResult.data;
|
|
966
|
+
|
|
967
|
+
// Load output submissions for each parent step run
|
|
968
|
+
const parentSubmissions = await Promise.all(
|
|
969
|
+
parentStepRuns.map(async (p) => {
|
|
970
|
+
const outputMapping =
|
|
971
|
+
await db.query.pipelineRunStepOutputSubmission.findFirst({
|
|
972
|
+
where: eq(schema.pipelineRunStepOutputSubmission.runStepId, p.id),
|
|
973
|
+
});
|
|
974
|
+
if (!outputMapping) return null;
|
|
975
|
+
return getDenormSubmission({
|
|
976
|
+
member,
|
|
977
|
+
submissionId: outputMapping.submissionId,
|
|
978
|
+
withCitations: true,
|
|
979
|
+
withTransformations: true,
|
|
980
|
+
});
|
|
981
|
+
}),
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
// Filter out nulls
|
|
985
|
+
const validParentSubmissions = parentSubmissions.filter(
|
|
986
|
+
(s): s is NonNullable<typeof s> => s !== null,
|
|
987
|
+
);
|
|
988
|
+
|
|
989
|
+
// Build request from template
|
|
990
|
+
const request = await buildHttpRequest({
|
|
991
|
+
config: {
|
|
992
|
+
method: integrationConfig.method as "GET" | "POST" | "PUT" | "DELETE",
|
|
993
|
+
urlTemplate: integrationConfig.urlTemplate,
|
|
994
|
+
headers: integrationConfig.headers,
|
|
995
|
+
bodyTemplate: integrationConfig.bodyTemplate ?? undefined,
|
|
996
|
+
},
|
|
997
|
+
parentData: validParentSubmissions,
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// Make API call
|
|
1001
|
+
const startTime = Date.now();
|
|
1002
|
+
let statusCode: number | undefined;
|
|
1003
|
+
let responseBody: unknown;
|
|
1004
|
+
let error: string | undefined;
|
|
1005
|
+
|
|
1006
|
+
try {
|
|
1007
|
+
const response = await fetch(request.url, {
|
|
1008
|
+
method: request.method,
|
|
1009
|
+
headers: request.headers,
|
|
1010
|
+
body: request.body,
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
statusCode = response.status;
|
|
1014
|
+
responseBody = await response.json();
|
|
1015
|
+
} catch (err) {
|
|
1016
|
+
error = err instanceof Error ? err.message : "Request failed";
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const durationMs = Date.now() - startTime;
|
|
1020
|
+
|
|
1021
|
+
// Log request call
|
|
1022
|
+
await logRequestCall({
|
|
1023
|
+
member,
|
|
1024
|
+
runStepId: runStep.id,
|
|
1025
|
+
method: request.method,
|
|
1026
|
+
url: request.url,
|
|
1027
|
+
requestHeaders: request.headers,
|
|
1028
|
+
requestBody: request.body,
|
|
1029
|
+
statusCode,
|
|
1030
|
+
responseBody,
|
|
1031
|
+
durationMs,
|
|
1032
|
+
error,
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
if (error || !statusCode || statusCode >= 400) {
|
|
1036
|
+
throw new Error(error || `HTTP ${statusCode}`);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// Parse response into output form if configured
|
|
1040
|
+
if (step.outputForms.length > 0) {
|
|
1041
|
+
// TODO: Parse responseBody into form structure and store
|
|
1042
|
+
// For now, mark as completed without storing output
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
await updatePipelineRunStepStatus({
|
|
1046
|
+
member,
|
|
1047
|
+
runStepId: runStep.id,
|
|
1048
|
+
status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
|
|
1049
|
+
});
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
await updatePipelineRunStepStatus({
|
|
1052
|
+
member,
|
|
1053
|
+
runStepId: runStep.id,
|
|
1054
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
1055
|
+
error: error instanceof Error ? error.message : "Integration step failed",
|
|
1056
|
+
});
|
|
1057
|
+
throw error;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Execute an analysis/transformation step (LLM-based)
|
|
1063
|
+
* Enhanced to support multi-parent inputs
|
|
1064
|
+
*/
|
|
1065
|
+
async function executeAnalysisStep({
|
|
1066
|
+
member,
|
|
1067
|
+
runStep,
|
|
1068
|
+
step,
|
|
1069
|
+
config,
|
|
1070
|
+
}: {
|
|
1071
|
+
member: Member;
|
|
1072
|
+
runStep: PipelineRunStep;
|
|
1073
|
+
step: typeof schema.pipelineStep.$inferSelect & {
|
|
1074
|
+
outputForms: Array<{
|
|
1075
|
+
id: string;
|
|
1076
|
+
stepId: string;
|
|
1077
|
+
formId: string;
|
|
1078
|
+
createdAt: Date;
|
|
1079
|
+
}>;
|
|
1080
|
+
};
|
|
1081
|
+
config: { model: ReturnType<typeof createLanguageModel> };
|
|
1082
|
+
}): Promise<void> {
|
|
1083
|
+
try {
|
|
1084
|
+
await updatePipelineRunStepStatus({
|
|
1085
|
+
member,
|
|
1086
|
+
runStepId: runStep.id,
|
|
1087
|
+
status: PIPELINE_RUN_STEP_STATUS.RUNNING,
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
// Validate output form exists
|
|
1091
|
+
if (step.outputForms.length === 0) {
|
|
1092
|
+
throw new Error("Analysis step missing output form");
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Get parent step runs with dependency configurations
|
|
1096
|
+
const parentStepRunsResult = await getParentStepRunsWithDependencies({
|
|
1097
|
+
member,
|
|
1098
|
+
runStepId: runStep.id,
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
if (!parentStepRunsResult.ok) {
|
|
1102
|
+
throw new Error("Failed to load parent step runs");
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const parentStepRuns = parentStepRunsResult.data;
|
|
1106
|
+
|
|
1107
|
+
// Load output submissions for each parent step run
|
|
1108
|
+
const parentSubmissions = await Promise.all(
|
|
1109
|
+
parentStepRuns.map(
|
|
1110
|
+
async (p: {
|
|
1111
|
+
id: string;
|
|
1112
|
+
stepId: string | null;
|
|
1113
|
+
status: string;
|
|
1114
|
+
formIdMapping: Record<string, string[]> | null;
|
|
1115
|
+
multiFormGroupId: string | null;
|
|
1116
|
+
}) => {
|
|
1117
|
+
const outputMapping =
|
|
1118
|
+
await db.query.pipelineRunStepOutputSubmission.findFirst({
|
|
1119
|
+
where: eq(schema.pipelineRunStepOutputSubmission.runStepId, p.id),
|
|
1120
|
+
});
|
|
1121
|
+
if (!outputMapping) return null;
|
|
1122
|
+
|
|
1123
|
+
const submission = await getDenormSubmission({
|
|
1124
|
+
member,
|
|
1125
|
+
submissionId: outputMapping.submissionId,
|
|
1126
|
+
withCitations: true,
|
|
1127
|
+
withTransformations: true,
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
if (!submission) return null;
|
|
1131
|
+
|
|
1132
|
+
// Apply formIdMapping filter if specified
|
|
1133
|
+
if (p.formIdMapping && Object.keys(p.formIdMapping).length > 0) {
|
|
1134
|
+
// Filter submission groups based on formIdMapping
|
|
1135
|
+
// formIdMapping is { [formId]: [groupId1, groupId2, ...] }
|
|
1136
|
+
const allowedGroupIds = new Set<string>();
|
|
1137
|
+
for (const groupIds of Object.values(p.formIdMapping)) {
|
|
1138
|
+
(groupIds as string[]).forEach((gid: string) =>
|
|
1139
|
+
allowedGroupIds.add(gid),
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// Filter groups to only include those in formIdMapping
|
|
1144
|
+
const filteredGroups = submission.groups.filter((g) =>
|
|
1145
|
+
allowedGroupIds.has(g.formGroupId),
|
|
1146
|
+
);
|
|
1147
|
+
|
|
1148
|
+
return { ...submission, groups: filteredGroups };
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return submission;
|
|
1152
|
+
},
|
|
1153
|
+
),
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
// Filter out nulls
|
|
1157
|
+
const validParentSubmissions = parentSubmissions.filter(
|
|
1158
|
+
(s): s is NonNullable<typeof s> => s !== null,
|
|
1159
|
+
);
|
|
1160
|
+
|
|
1161
|
+
// Check if this is a multi step - if so, filter parent data to specific group
|
|
1162
|
+
const groupResult = await getGroupForStepRun({
|
|
1163
|
+
member,
|
|
1164
|
+
runStepId: runStep.id,
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
let filteredParentSubmissions = validParentSubmissions;
|
|
1168
|
+
if (groupResult.ok) {
|
|
1169
|
+
// This is a multi step, filter to the specific group
|
|
1170
|
+
const targetGroup = groupResult.data;
|
|
1171
|
+
|
|
1172
|
+
// Build a map of which parent steps have the multiFormGroupId
|
|
1173
|
+
const parentStepsWithMultiGroup = new Set<string>();
|
|
1174
|
+
for (const parent of parentStepRuns) {
|
|
1175
|
+
if (parent.multiFormGroupId === targetGroup.formGroupId) {
|
|
1176
|
+
parentStepsWithMultiGroup.add(parent.id);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Filter each parent submission to only include the specific group instance
|
|
1181
|
+
// BUT only if that parent has the multiFormGroupId configured
|
|
1182
|
+
filteredParentSubmissions = validParentSubmissions.map(
|
|
1183
|
+
(submission, idx) => {
|
|
1184
|
+
const parentStepRun = parentStepRuns[idx];
|
|
1185
|
+
if (!parentStepRun) return submission;
|
|
1186
|
+
|
|
1187
|
+
// If this parent doesn't have the multiFormGroupId, return full data
|
|
1188
|
+
if (!parentStepsWithMultiGroup.has(parentStepRun.id)) {
|
|
1189
|
+
return submission;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// This parent has the multiFormGroupId, so filter to the specific group
|
|
1193
|
+
const matchingGroup = submission.groups.find(
|
|
1194
|
+
(g) => g.formGroupId === targetGroup.formGroupId,
|
|
1195
|
+
);
|
|
1196
|
+
|
|
1197
|
+
if (!matchingGroup) {
|
|
1198
|
+
// No matching group, return submission with empty groups
|
|
1199
|
+
return { ...submission, groups: [] };
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// Return submission with only the matching group
|
|
1203
|
+
return { ...submission, groups: [matchingGroup] };
|
|
1204
|
+
},
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// Create output submission
|
|
1209
|
+
const sourceSubmission = validParentSubmissions[0];
|
|
1210
|
+
if (!sourceSubmission) {
|
|
1211
|
+
throw new Error("No parent submissions found");
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// Get full submission to access dealId
|
|
1215
|
+
const fullSubmissionResult = await getRuntimeSubmission({
|
|
1216
|
+
member,
|
|
1217
|
+
submissionId: sourceSubmission.id,
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
if (!fullSubmissionResult.ok) {
|
|
1221
|
+
throw new Error("Failed to load source submission");
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
const outputFormId = step.outputForms[0]?.formId;
|
|
1225
|
+
if (!outputFormId) {
|
|
1226
|
+
throw new Error("Analysis step missing output form");
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const outputSubmission = await createSubmissionWithConversation({
|
|
1230
|
+
member,
|
|
1231
|
+
data: {
|
|
1232
|
+
formId: outputFormId,
|
|
1233
|
+
dealId: fullSubmissionResult.data.dealId,
|
|
1234
|
+
name:
|
|
1235
|
+
runStep.spawnType === "instance"
|
|
1236
|
+
? `${step.name} - Instance ${runStep.stepRunIndex! + 1}`
|
|
1237
|
+
: `${step.name} output`,
|
|
1238
|
+
status: "draft",
|
|
1239
|
+
type: "transformation",
|
|
1240
|
+
},
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
await syncSubmissionToForm({
|
|
1244
|
+
member,
|
|
1245
|
+
submissionId: outputSubmission.id,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
// Get output form groups
|
|
1249
|
+
const outputSubmissionWithData = await getSubmissionWithData({
|
|
1250
|
+
member,
|
|
1251
|
+
submissionId: outputSubmission.id,
|
|
1252
|
+
typeFilter: "transformation",
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
if (!outputSubmissionWithData?.form?.groups) {
|
|
1256
|
+
throw new Error("Failed to load output form");
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const outputFormGroups = outputSubmissionWithData.form.groups;
|
|
1260
|
+
|
|
1261
|
+
// If this is a spawned instance, extract the spawning group data
|
|
1262
|
+
let spawningGroupData: {
|
|
1263
|
+
groupName: string;
|
|
1264
|
+
data: DenormSubmission["groups"][0];
|
|
1265
|
+
} | null = null;
|
|
1266
|
+
|
|
1267
|
+
if (groupResult.ok) {
|
|
1268
|
+
const targetGroup = groupResult.data;
|
|
1269
|
+
// Find the spawning group in the filtered parent submissions
|
|
1270
|
+
for (const submission of filteredParentSubmissions) {
|
|
1271
|
+
const matchingGroup = submission.groups.find(
|
|
1272
|
+
(g) => g.formGroupId === targetGroup.formGroupId,
|
|
1273
|
+
);
|
|
1274
|
+
if (matchingGroup) {
|
|
1275
|
+
spawningGroupData = {
|
|
1276
|
+
groupName: matchingGroup.name,
|
|
1277
|
+
data: matchingGroup,
|
|
1278
|
+
};
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Run the LLM-based analysis
|
|
1285
|
+
const stepResult = await runStepExecute({
|
|
1286
|
+
member,
|
|
1287
|
+
runId: runStep.runId,
|
|
1288
|
+
runStepId: runStep.id,
|
|
1289
|
+
step: {
|
|
1290
|
+
id: step.id,
|
|
1291
|
+
description: step.description,
|
|
1292
|
+
prompt: step.prompt,
|
|
1293
|
+
toolsAllowed: step.toolsAllowed,
|
|
1294
|
+
outputFormIds: step.outputForms.map((of) => of.formId),
|
|
1295
|
+
// Legacy fields - no longer used in new architecture but required by function signature
|
|
1296
|
+
stepOrder: 0,
|
|
1297
|
+
parentStepId: null,
|
|
1298
|
+
},
|
|
1299
|
+
inputSubmissions: filteredParentSubmissions,
|
|
1300
|
+
resourceSubmissions: [], // TODO: Load pipeline resources
|
|
1301
|
+
outputFormGroups,
|
|
1302
|
+
conversationId: outputSubmission.conversationId,
|
|
1303
|
+
spawningGroupData,
|
|
1304
|
+
config,
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
// Store output
|
|
1308
|
+
if (stepResult.output?.data) {
|
|
1309
|
+
const storeResult = await storePipelineOutputData({
|
|
1310
|
+
member,
|
|
1311
|
+
outputSubmissionId: outputSubmission.id,
|
|
1312
|
+
outputData: stepResult.output.data,
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
if (!storeResult.ok) {
|
|
1316
|
+
throw new Error("Failed to store pipeline output data");
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// Link output submission to run step
|
|
1321
|
+
await db.insert(schema.pipelineRunStepOutputSubmission).values({
|
|
1322
|
+
runStepId: runStep.id,
|
|
1323
|
+
submissionId: outputSubmission.id,
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
await updatePipelineRunStepStatus({
|
|
1327
|
+
member,
|
|
1328
|
+
runStepId: runStep.id,
|
|
1329
|
+
status: stepResult.status,
|
|
1330
|
+
error: stepResult.error,
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
if (stepResult.status === "failed") {
|
|
1334
|
+
throw new Error(stepResult.error || "Analysis step failed");
|
|
1335
|
+
}
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
await updatePipelineRunStepStatus({
|
|
1338
|
+
member,
|
|
1339
|
+
runStepId: runStep.id,
|
|
1340
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
1341
|
+
error: error instanceof Error ? error.message : "Analysis step failed",
|
|
1342
|
+
});
|
|
1343
|
+
throw error;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Execute a manual step (waits for human input)
|
|
1349
|
+
*/
|
|
1350
|
+
async function executeManualStep({
|
|
1351
|
+
member,
|
|
1352
|
+
runStep,
|
|
1353
|
+
step,
|
|
1354
|
+
}: {
|
|
1355
|
+
member: Member;
|
|
1356
|
+
runStep: PipelineRunStep;
|
|
1357
|
+
step: typeof schema.pipelineStep.$inferSelect & {
|
|
1358
|
+
outputForms: Array<{
|
|
1359
|
+
id: string;
|
|
1360
|
+
stepId: string;
|
|
1361
|
+
formId: string;
|
|
1362
|
+
createdAt: Date;
|
|
1363
|
+
}>;
|
|
1364
|
+
};
|
|
1365
|
+
}): Promise<void> {
|
|
1366
|
+
try {
|
|
1367
|
+
await updatePipelineRunStepStatus({
|
|
1368
|
+
member,
|
|
1369
|
+
runStepId: runStep.id,
|
|
1370
|
+
status: PIPELINE_RUN_STEP_STATUS.RUNNING,
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
// Check if manual output submission has been provided
|
|
1374
|
+
const outputMapping =
|
|
1375
|
+
await db.query.pipelineRunStepOutputSubmission.findFirst({
|
|
1376
|
+
where: eq(schema.pipelineRunStepOutputSubmission.runStepId, runStep.id),
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
if (!outputMapping) {
|
|
1380
|
+
// No output submission yet - create a blocker
|
|
1381
|
+
console.log(
|
|
1382
|
+
`[executeManualStep] Step ${step.name} blocked - waiting for manual output submission`,
|
|
1383
|
+
);
|
|
1384
|
+
|
|
1385
|
+
// Check if blocker already exists
|
|
1386
|
+
const existingBlocker = await db.query.pipelineRunStepBlocker.findFirst({
|
|
1387
|
+
where: and(
|
|
1388
|
+
eq(schema.pipelineRunStepBlocker.runStepId, runStep.id),
|
|
1389
|
+
eq(schema.pipelineRunStepBlocker.blockerType, "missing_manual_input"),
|
|
1390
|
+
eq(schema.pipelineRunStepBlocker.resolved, false),
|
|
1391
|
+
),
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
if (!existingBlocker) {
|
|
1395
|
+
await db.insert(schema.pipelineRunStepBlocker).values({
|
|
1396
|
+
runStepId: runStep.id,
|
|
1397
|
+
blockerType: "missing_manual_input",
|
|
1398
|
+
blockerDetails: {
|
|
1399
|
+
type: "missing_manual_input",
|
|
1400
|
+
stepName: step.name,
|
|
1401
|
+
stepDescription: step.description,
|
|
1402
|
+
},
|
|
1403
|
+
resolved: false,
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
await updatePipelineRunStepStatus({
|
|
1408
|
+
member,
|
|
1409
|
+
runStepId: runStep.id,
|
|
1410
|
+
status: PIPELINE_RUN_STEP_STATUS.BLOCKED,
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// Output submission exists - mark as completed
|
|
1417
|
+
await updatePipelineRunStepStatus({
|
|
1418
|
+
member,
|
|
1419
|
+
runStepId: runStep.id,
|
|
1420
|
+
status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
console.log(
|
|
1424
|
+
`[executeManualStep] Step ${step.name} completed - manual output submission provided`,
|
|
1425
|
+
);
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
console.error("[executeManualStep] Error:", error);
|
|
1428
|
+
await updatePipelineRunStepStatus({
|
|
1429
|
+
member,
|
|
1430
|
+
runStepId: runStep.id,
|
|
1431
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
1432
|
+
error: error instanceof Error ? error.message : "Manual step failed",
|
|
1433
|
+
});
|
|
1434
|
+
throw error;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/**
|
|
1439
|
+
* Spawn instances for a template step based on parent multi-group output
|
|
1440
|
+
*/
|
|
1441
|
+
async function spawnInstancesForTemplate({
|
|
1442
|
+
member,
|
|
1443
|
+
runId,
|
|
1444
|
+
runStep,
|
|
1445
|
+
step,
|
|
1446
|
+
}: {
|
|
1447
|
+
member: Member;
|
|
1448
|
+
runId: string;
|
|
1449
|
+
runStep: PipelineRunStep;
|
|
1450
|
+
step: typeof schema.pipelineStep.$inferSelect & {
|
|
1451
|
+
outputForms: Array<{
|
|
1452
|
+
id: string;
|
|
1453
|
+
stepId: string;
|
|
1454
|
+
formId: string;
|
|
1455
|
+
createdAt: Date;
|
|
1456
|
+
}>;
|
|
1457
|
+
};
|
|
1458
|
+
}): Promise<void> {
|
|
1459
|
+
console.log(
|
|
1460
|
+
`[spawnInstancesForTemplate] Starting spawn for template step ${step.id}`,
|
|
1461
|
+
);
|
|
1462
|
+
|
|
1463
|
+
// Get parent step runs with dependency info
|
|
1464
|
+
const parentStepRunsResult = await getParentStepRunsWithDependencies({
|
|
1465
|
+
member,
|
|
1466
|
+
runStepId: runStep.id,
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
if (!parentStepRunsResult.ok) {
|
|
1470
|
+
console.error(
|
|
1471
|
+
`[spawnInstancesForTemplate] Failed to get parent dependencies for step ${step.id}`,
|
|
1472
|
+
);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
const parentStepRuns = parentStepRunsResult.data;
|
|
1477
|
+
console.log(
|
|
1478
|
+
`[spawnInstancesForTemplate] Found ${parentStepRuns.length} parent step(s)`,
|
|
1479
|
+
);
|
|
1480
|
+
|
|
1481
|
+
// Find parent with multiFormGroupId
|
|
1482
|
+
const multiParent = parentStepRuns.find((p) => p.multiFormGroupId !== null);
|
|
1483
|
+
|
|
1484
|
+
if (!multiParent) {
|
|
1485
|
+
console.warn(
|
|
1486
|
+
`[spawnInstancesForTemplate] No multi-group parent found for template step ${step.id}`,
|
|
1487
|
+
);
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
console.log(
|
|
1492
|
+
`[spawnInstancesForTemplate] Found multi-group parent with multiFormGroupId: ${multiParent.multiFormGroupId}`,
|
|
1493
|
+
);
|
|
1494
|
+
|
|
1495
|
+
// Get parent output submission
|
|
1496
|
+
const parentOutputMapping =
|
|
1497
|
+
await db.query.pipelineRunStepOutputSubmission.findFirst({
|
|
1498
|
+
where: eq(
|
|
1499
|
+
schema.pipelineRunStepOutputSubmission.runStepId,
|
|
1500
|
+
multiParent.id,
|
|
1501
|
+
),
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
if (!parentOutputMapping) {
|
|
1505
|
+
console.error(
|
|
1506
|
+
`[spawnInstancesForTemplate] No output submission found for parent step ${multiParent.id}`,
|
|
1507
|
+
);
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
console.log(
|
|
1512
|
+
`[spawnInstancesForTemplate] Parent output submission: ${parentOutputMapping.submissionId}`,
|
|
1513
|
+
);
|
|
1514
|
+
|
|
1515
|
+
// Get submission groups using DAL function
|
|
1516
|
+
const groupsResult = await getSubmissionGroups({
|
|
1517
|
+
member,
|
|
1518
|
+
submissionId: parentOutputMapping.submissionId,
|
|
1519
|
+
formGroupId: multiParent.multiFormGroupId!,
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
if (!groupsResult.ok || groupsResult.data.length === 0) {
|
|
1523
|
+
console.log(
|
|
1524
|
+
`[spawnInstancesForTemplate] No groups found for formGroupId ${multiParent.multiFormGroupId}`,
|
|
1525
|
+
);
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
const groups = groupsResult.data;
|
|
1530
|
+
console.log(
|
|
1531
|
+
`[spawnInstancesForTemplate] Spawning ${groups.length} instance(s) for step ${step.id}`,
|
|
1532
|
+
);
|
|
1533
|
+
|
|
1534
|
+
// Create instance run steps for each group
|
|
1535
|
+
for (const [index, group] of groups.entries()) {
|
|
1536
|
+
console.log(
|
|
1537
|
+
`[spawnInstancesForTemplate] Creating instance ${index + 1}/${groups.length} for group ${group.id}`,
|
|
1538
|
+
);
|
|
1539
|
+
|
|
1540
|
+
await createPipelineRunStep({
|
|
1541
|
+
member,
|
|
1542
|
+
runId,
|
|
1543
|
+
stepId: step.id,
|
|
1544
|
+
spawnType: "instance",
|
|
1545
|
+
submissionGroupId: group.id,
|
|
1546
|
+
stepRunIndex: index,
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
console.log(
|
|
1551
|
+
`[spawnInstancesForTemplate] Successfully spawned ${groups.length} instance(s) for step ${step.id}`,
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* Check if step output has multiple groups and spawn instance run steps for child steps
|
|
1557
|
+
*/
|
|
1558
|
+
async function checkAndSpawnMultiGroups({
|
|
1559
|
+
member,
|
|
1560
|
+
runStep,
|
|
1561
|
+
step,
|
|
1562
|
+
}: {
|
|
1563
|
+
member: Member;
|
|
1564
|
+
runStep: PipelineRunStep;
|
|
1565
|
+
step: typeof schema.pipelineStep.$inferSelect;
|
|
1566
|
+
}): Promise<void> {
|
|
1567
|
+
try {
|
|
1568
|
+
// Only extraction and analysis steps can produce multi-group outputs
|
|
1569
|
+
if (
|
|
1570
|
+
step.role !== "extraction" &&
|
|
1571
|
+
step.role !== "analysis" &&
|
|
1572
|
+
step.role !== null
|
|
1573
|
+
) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// Get output submission if exists
|
|
1578
|
+
const outputMapping =
|
|
1579
|
+
await db.query.pipelineRunStepOutputSubmission.findFirst({
|
|
1580
|
+
where: eq(schema.pipelineRunStepOutputSubmission.runStepId, runStep.id),
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
if (!outputMapping) {
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// Get child steps that depend on this step with multiFormGroupId
|
|
1588
|
+
const childDepsResult = await getStepDependenciesByParentStepId({
|
|
1589
|
+
member,
|
|
1590
|
+
parentStepId: step.id,
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
if (!childDepsResult.ok || childDepsResult.data.length === 0) {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
const multiDeps = childDepsResult.data.filter((d) => d.multiFormGroupId);
|
|
1598
|
+
|
|
1599
|
+
if (multiDeps.length === 0) {
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
console.log(
|
|
1604
|
+
`[checkAndSpawnMultiGroups] Found ${multiDeps.length} multi-dependency child step(s)`,
|
|
1605
|
+
);
|
|
1606
|
+
|
|
1607
|
+
for (const dep of multiDeps) {
|
|
1608
|
+
// Find the template run step that was created at run creation
|
|
1609
|
+
const templateRunStep = await db.query.pipelineRunStep.findFirst({
|
|
1610
|
+
where: and(
|
|
1611
|
+
eq(schema.pipelineRunStep.runId, runStep.runId),
|
|
1612
|
+
eq(schema.pipelineRunStep.stepId, dep.childStepId),
|
|
1613
|
+
eq(schema.pipelineRunStep.spawnType, "template"),
|
|
1614
|
+
),
|
|
1615
|
+
});
|
|
1616
|
+
|
|
1617
|
+
if (!templateRunStep) {
|
|
1618
|
+
console.warn(
|
|
1619
|
+
`[checkAndSpawnMultiGroups] Template run step not found for child step ${dep.childStepId}`,
|
|
1620
|
+
);
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Get submission groups using new DAL function
|
|
1625
|
+
const groupsResult = await getSubmissionGroups({
|
|
1626
|
+
member,
|
|
1627
|
+
submissionId: outputMapping.submissionId,
|
|
1628
|
+
formGroupId: dep.multiFormGroupId!,
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
if (!groupsResult.ok || groupsResult.data.length === 0) {
|
|
1632
|
+
console.log(
|
|
1633
|
+
`[checkAndSpawnMultiGroups] No groups found for formGroupId ${dep.multiFormGroupId}`,
|
|
1634
|
+
);
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
const groups = groupsResult.data;
|
|
1639
|
+
console.log(
|
|
1640
|
+
`[checkAndSpawnMultiGroups] Spawning ${groups.length} instance(s) for step ${dep.childStepId}`,
|
|
1641
|
+
);
|
|
1642
|
+
|
|
1643
|
+
// Create instance run steps for each group
|
|
1644
|
+
for (const [index, group] of groups.entries()) {
|
|
1645
|
+
await createPipelineRunStep({
|
|
1646
|
+
member,
|
|
1647
|
+
runId: runStep.runId,
|
|
1648
|
+
stepId: dep.childStepId,
|
|
1649
|
+
spawnType: "instance",
|
|
1650
|
+
submissionGroupId: group.id,
|
|
1651
|
+
stepRunIndex: index,
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// Mark template as spawned
|
|
1656
|
+
await updatePipelineRunStepStatus({
|
|
1657
|
+
member,
|
|
1658
|
+
runStepId: templateRunStep.id,
|
|
1659
|
+
status: "spawned",
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
console.log(
|
|
1663
|
+
`[checkAndSpawnMultiGroups] Successfully spawned ${groups.length} instance(s) for step ${dep.childStepId}`,
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
console.error("[checkAndSpawnMultiGroups] Error:", error);
|
|
1668
|
+
// Don't throw - this is non-critical, log and continue
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
/**
|
|
1673
|
+
* Build HTTP request from integration config template with variable substitution
|
|
1674
|
+
*/
|
|
1675
|
+
async function buildHttpRequest({
|
|
1676
|
+
config,
|
|
1677
|
+
parentData,
|
|
1678
|
+
}: {
|
|
1679
|
+
config: {
|
|
1680
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
1681
|
+
urlTemplate: string;
|
|
1682
|
+
headers: Record<string, string>;
|
|
1683
|
+
bodyTemplate?: string;
|
|
1684
|
+
};
|
|
1685
|
+
parentData: DenormSubmission[];
|
|
1686
|
+
}): Promise<{
|
|
1687
|
+
method: string;
|
|
1688
|
+
url: string;
|
|
1689
|
+
headers: Record<string, string>;
|
|
1690
|
+
body?: string;
|
|
1691
|
+
}> {
|
|
1692
|
+
// Build variable map from parent submissions
|
|
1693
|
+
const variables = new Map<string, string>();
|
|
1694
|
+
|
|
1695
|
+
for (const submission of parentData) {
|
|
1696
|
+
// Add form-level variables
|
|
1697
|
+
variables.set(`form.name`, submission.formName);
|
|
1698
|
+
|
|
1699
|
+
// Add field-level variables from groups
|
|
1700
|
+
for (const group of submission.groups) {
|
|
1701
|
+
for (const item of group.items) {
|
|
1702
|
+
// Variable pattern: groupName.fieldName
|
|
1703
|
+
const key = `${group.name}.${item.name}`;
|
|
1704
|
+
const value = String(item.value ?? "");
|
|
1705
|
+
variables.set(key, value);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// Also add derived fields
|
|
1709
|
+
for (const derived of group.derived) {
|
|
1710
|
+
const key = `${group.name}.${derived.name}`;
|
|
1711
|
+
const value = String(derived.value ?? "");
|
|
1712
|
+
variables.set(key, value);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// Add table row variables (use first row for simplicity)
|
|
1717
|
+
for (const table of submission.tables) {
|
|
1718
|
+
const firstRow = table.rows[0];
|
|
1719
|
+
if (firstRow) {
|
|
1720
|
+
for (const item of firstRow.items) {
|
|
1721
|
+
const key = `${table.name}.${item.name}`;
|
|
1722
|
+
const value = String(item.value ?? "");
|
|
1723
|
+
variables.set(key, value);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// Substitute variables in URL template
|
|
1730
|
+
// Pattern: {{variableName}} or {{groupName.fieldName}}
|
|
1731
|
+
const substituteVariables = (template: string): string => {
|
|
1732
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (match, varName) => {
|
|
1733
|
+
const trimmedName = varName.trim();
|
|
1734
|
+
const value = variables.get(trimmedName);
|
|
1735
|
+
|
|
1736
|
+
if (value !== undefined) {
|
|
1737
|
+
return encodeURIComponent(value);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
console.warn(`[buildHttpRequest] Variable not found: ${trimmedName}`);
|
|
1741
|
+
return match; // Keep original if not found
|
|
1742
|
+
});
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
const url = substituteVariables(config.urlTemplate);
|
|
1746
|
+
|
|
1747
|
+
// Substitute in headers
|
|
1748
|
+
const headers: Record<string, string> = {};
|
|
1749
|
+
for (const [key, value] of Object.entries(config.headers)) {
|
|
1750
|
+
headers[key] = substituteVariables(value);
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// Substitute in body
|
|
1754
|
+
const body = config.bodyTemplate
|
|
1755
|
+
? substituteVariables(config.bodyTemplate)
|
|
1756
|
+
: undefined;
|
|
1757
|
+
|
|
1758
|
+
return {
|
|
1759
|
+
method: config.method,
|
|
1760
|
+
url,
|
|
1761
|
+
headers,
|
|
1762
|
+
body,
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// ============================================================================
|
|
1767
|
+
// Single Step Execution (Internal)
|
|
1768
|
+
// ============================================================================
|
|
1769
|
+
|
|
1770
|
+
type ExecuteSingleStepResult =
|
|
1771
|
+
| {
|
|
1772
|
+
ok: true;
|
|
1773
|
+
data: { outputSubmissionIds: string[]; runSteps: PipelineRunStep[] };
|
|
1774
|
+
}
|
|
1775
|
+
| { ok: false; error: string };
|
|
1776
|
+
|
|
1777
|
+
export async function executeSingleStep({
|
|
1778
|
+
member,
|
|
1779
|
+
runId,
|
|
1780
|
+
pipelineId,
|
|
1781
|
+
step,
|
|
1782
|
+
inputSubmissionId,
|
|
1783
|
+
allInputSubmissionIds,
|
|
1784
|
+
config,
|
|
1785
|
+
}: {
|
|
1786
|
+
member: Member;
|
|
1787
|
+
runId: string;
|
|
1788
|
+
pipelineId: string;
|
|
1789
|
+
step: {
|
|
1790
|
+
id: string;
|
|
1791
|
+
stepOrder: number;
|
|
1792
|
+
type: "single" | "multi";
|
|
1793
|
+
description: string;
|
|
1794
|
+
prompt: string;
|
|
1795
|
+
toolsAllowed: string[] | null;
|
|
1796
|
+
parentStepId: string | null;
|
|
1797
|
+
outputFormIds: string[];
|
|
1798
|
+
};
|
|
1799
|
+
inputSubmissionId: string;
|
|
1800
|
+
allInputSubmissionIds?: string[];
|
|
1801
|
+
config: {
|
|
1802
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
1803
|
+
};
|
|
1804
|
+
}): Promise<ExecuteSingleStepResult> {
|
|
1805
|
+
console.log("[executeSingleStep] Starting step execution:", {
|
|
1806
|
+
stepId: step.id,
|
|
1807
|
+
stepOrder: step.stepOrder,
|
|
1808
|
+
stepType: step.type,
|
|
1809
|
+
inputSubmissionId,
|
|
1810
|
+
allInputSubmissionIds,
|
|
1811
|
+
allInputCount: allInputSubmissionIds?.length,
|
|
1812
|
+
});
|
|
1813
|
+
|
|
1814
|
+
// 1. Validate step has output form
|
|
1815
|
+
if (!step.outputFormIds || step.outputFormIds.length === 0) {
|
|
1816
|
+
return { ok: false, error: `Step ${step.stepOrder} has no output form` };
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// 2. Validate input submission exists
|
|
1820
|
+
console.log(
|
|
1821
|
+
"[executeSingleStep] Validating primary input submission:",
|
|
1822
|
+
inputSubmissionId,
|
|
1823
|
+
);
|
|
1824
|
+
const inputSubmissionResult = await getRuntimeSubmission({
|
|
1825
|
+
member,
|
|
1826
|
+
submissionId: inputSubmissionId,
|
|
1827
|
+
});
|
|
1828
|
+
if (!inputSubmissionResult.ok) {
|
|
1829
|
+
console.error("[executeSingleStep] Primary input submission not found:", {
|
|
1830
|
+
inputSubmissionId,
|
|
1831
|
+
organizationId: member.organizationId,
|
|
1832
|
+
});
|
|
1833
|
+
return { ok: false, error: "Input submission not found" };
|
|
1834
|
+
}
|
|
1835
|
+
console.log("[executeSingleStep] Primary input submission found:", {
|
|
1836
|
+
id: inputSubmissionResult.data.id,
|
|
1837
|
+
name: inputSubmissionResult.data.name,
|
|
1838
|
+
formId: inputSubmissionResult.data.formId,
|
|
1839
|
+
});
|
|
1840
|
+
const sourceSubmission = inputSubmissionResult.data;
|
|
1841
|
+
|
|
1842
|
+
// 3. For multi-step, get the input submission data to find multi groups
|
|
1843
|
+
let multiGroupEntries: Array<{ order: number; data: unknown }> = [];
|
|
1844
|
+
if (step.type === "multi") {
|
|
1845
|
+
const inputSubmission = await getDenormSubmission({
|
|
1846
|
+
member,
|
|
1847
|
+
submissionId: inputSubmissionId,
|
|
1848
|
+
withCitations: true,
|
|
1849
|
+
withTransformations: true,
|
|
1850
|
+
});
|
|
1851
|
+
|
|
1852
|
+
// Find the first multi group in tables
|
|
1853
|
+
const firstMultiTable = inputSubmission.tables[0];
|
|
1854
|
+
if (!firstMultiTable || firstMultiTable.rows.length === 0) {
|
|
1855
|
+
return {
|
|
1856
|
+
ok: false,
|
|
1857
|
+
error:
|
|
1858
|
+
"Multi-step requires input with at least one multi group with entries",
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// Create an entry for each row in the multi group
|
|
1863
|
+
multiGroupEntries = firstMultiTable.rows.map((row, idx) => ({
|
|
1864
|
+
order: idx,
|
|
1865
|
+
data: row,
|
|
1866
|
+
}));
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// 4. Determine how many output submissions to create
|
|
1870
|
+
const outputCount = step.type === "multi" ? multiGroupEntries.length : 1;
|
|
1871
|
+
const outputSubmissions: Array<{
|
|
1872
|
+
id: string;
|
|
1873
|
+
dealId: string;
|
|
1874
|
+
conversationId: string;
|
|
1875
|
+
}> = [];
|
|
1876
|
+
const runSteps: PipelineRunStep[] = [];
|
|
1877
|
+
|
|
1878
|
+
// 5. Create output submissions and run steps
|
|
1879
|
+
for (let i = 0; i < outputCount; i++) {
|
|
1880
|
+
const outputFormId = step.outputFormIds[0];
|
|
1881
|
+
if (!outputFormId) {
|
|
1882
|
+
return { ok: false, error: `Step ${step.stepOrder} has no output form` };
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
const outputSubmission = await createSubmissionWithConversation({
|
|
1886
|
+
member,
|
|
1887
|
+
data: {
|
|
1888
|
+
formId: outputFormId,
|
|
1889
|
+
dealId: sourceSubmission.dealId,
|
|
1890
|
+
name:
|
|
1891
|
+
step.type === "multi"
|
|
1892
|
+
? `Step ${step.stepOrder} - Entry ${i + 1}`
|
|
1893
|
+
: `Step ${step.stepOrder}`,
|
|
1894
|
+
status: "draft",
|
|
1895
|
+
type: "transformation",
|
|
1896
|
+
},
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
// Sync submission to form to create groups and items
|
|
1900
|
+
await syncSubmissionToForm({
|
|
1901
|
+
member,
|
|
1902
|
+
submissionId: outputSubmission.id,
|
|
1903
|
+
});
|
|
1904
|
+
|
|
1905
|
+
outputSubmissions.push(outputSubmission);
|
|
1906
|
+
|
|
1907
|
+
// Create run step in DB
|
|
1908
|
+
const runStepResult = await createPipelineRunStep({
|
|
1909
|
+
member,
|
|
1910
|
+
runId,
|
|
1911
|
+
stepId: step.id,
|
|
1912
|
+
});
|
|
1913
|
+
if (!runStepResult.ok) {
|
|
1914
|
+
return { ok: false, error: "Failed to create run step" };
|
|
1915
|
+
}
|
|
1916
|
+
const runStep = runStepResult.data;
|
|
1917
|
+
runSteps.push(runStep);
|
|
1918
|
+
|
|
1919
|
+
// Link output submission to run step
|
|
1920
|
+
await db.insert(schema.pipelineRunStepOutputSubmission).values({
|
|
1921
|
+
runStepId: runStep.id,
|
|
1922
|
+
submissionId: outputSubmission.id,
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// 6. Execute each output submission in parallel (with concurrency limit of 10)
|
|
1927
|
+
const CONCURRENCY_LIMIT = 10;
|
|
1928
|
+
const executeSubmission = async (
|
|
1929
|
+
outputSubmission: (typeof outputSubmissions)[number],
|
|
1930
|
+
i: number,
|
|
1931
|
+
) => {
|
|
1932
|
+
const runStep = runSteps[i];
|
|
1933
|
+
|
|
1934
|
+
if (!outputSubmission || !runStep) {
|
|
1935
|
+
throw new Error("Internal error: missing output submission or run step");
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
try {
|
|
1939
|
+
// Update status to running
|
|
1940
|
+
await updatePipelineRunStepStatus({
|
|
1941
|
+
member,
|
|
1942
|
+
runStepId: runStep.id,
|
|
1943
|
+
status: PIPELINE_RUN_STEP_STATUS.RUNNING,
|
|
1944
|
+
});
|
|
1945
|
+
|
|
1946
|
+
// Get input submission data with citations
|
|
1947
|
+
// For single-step after multi-step: load ALL input submissions
|
|
1948
|
+
const inputSubmissionsToLoad = allInputSubmissionIds || [
|
|
1949
|
+
inputSubmissionId,
|
|
1950
|
+
];
|
|
1951
|
+
console.log(
|
|
1952
|
+
"[executeSingleStep] Loading input submissions for execution:",
|
|
1953
|
+
{
|
|
1954
|
+
count: inputSubmissionsToLoad.length,
|
|
1955
|
+
ids: inputSubmissionsToLoad,
|
|
1956
|
+
},
|
|
1957
|
+
);
|
|
1958
|
+
|
|
1959
|
+
const inputSubmissions = await Promise.all(
|
|
1960
|
+
inputSubmissionsToLoad.map(async (id) => {
|
|
1961
|
+
console.log("[executeSingleStep] Loading submission:", id);
|
|
1962
|
+
try {
|
|
1963
|
+
const submission = await getDenormSubmission({
|
|
1964
|
+
member,
|
|
1965
|
+
submissionId: id,
|
|
1966
|
+
withCitations: true,
|
|
1967
|
+
withTransformations: true,
|
|
1968
|
+
});
|
|
1969
|
+
console.log("[executeSingleStep] Successfully loaded submission:", {
|
|
1970
|
+
id,
|
|
1971
|
+
name: submission.name,
|
|
1972
|
+
});
|
|
1973
|
+
return submission;
|
|
1974
|
+
} catch (error) {
|
|
1975
|
+
console.error("[executeSingleStep] Failed to load submission:", {
|
|
1976
|
+
id,
|
|
1977
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1978
|
+
});
|
|
1979
|
+
throw error;
|
|
1980
|
+
}
|
|
1981
|
+
}),
|
|
1982
|
+
);
|
|
1983
|
+
console.log(
|
|
1984
|
+
"[executeSingleStep] All input submissions loaded successfully",
|
|
1985
|
+
);
|
|
1986
|
+
|
|
1987
|
+
// Get output form groups
|
|
1988
|
+
const outputSubmissionWithData = await getSubmissionWithData({
|
|
1989
|
+
member,
|
|
1990
|
+
submissionId: outputSubmission.id,
|
|
1991
|
+
typeFilter: "transformation",
|
|
1992
|
+
});
|
|
1993
|
+
if (!outputSubmissionWithData?.form?.groups) {
|
|
1994
|
+
throw new Error("Failed to load output form");
|
|
1995
|
+
}
|
|
1996
|
+
const outputFormGroups = outputSubmissionWithData.form.groups;
|
|
1997
|
+
|
|
1998
|
+
// Run the step execution
|
|
1999
|
+
const stepResult = await runStepExecute({
|
|
2000
|
+
member,
|
|
2001
|
+
runId,
|
|
2002
|
+
runStepId: runStep.id,
|
|
2003
|
+
step,
|
|
2004
|
+
inputSubmissions,
|
|
2005
|
+
resourceSubmissions: [],
|
|
2006
|
+
outputFormGroups,
|
|
2007
|
+
conversationId: outputSubmission.conversationId,
|
|
2008
|
+
config,
|
|
2009
|
+
});
|
|
2010
|
+
|
|
2011
|
+
// Write output using storePipelineOutputData
|
|
2012
|
+
if (stepResult.output?.data) {
|
|
2013
|
+
const storeResult = await storePipelineOutputData({
|
|
2014
|
+
member,
|
|
2015
|
+
outputSubmissionId: outputSubmission.id,
|
|
2016
|
+
outputData: stepResult.output.data,
|
|
2017
|
+
});
|
|
2018
|
+
if (!storeResult.ok) {
|
|
2019
|
+
throw new Error("Failed to store pipeline output data");
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// Update step status
|
|
2024
|
+
await updatePipelineRunStepStatus({
|
|
2025
|
+
member,
|
|
2026
|
+
runStepId: runStep.id,
|
|
2027
|
+
status: stepResult.status,
|
|
2028
|
+
error: stepResult.error,
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
if (stepResult.status === "failed") {
|
|
2032
|
+
throw new Error(stepResult.error ?? "Step failed");
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
return outputSubmission.id;
|
|
2036
|
+
} catch (error) {
|
|
2037
|
+
const errorMessage =
|
|
2038
|
+
error instanceof Error ? error.message : "Step execution failed";
|
|
2039
|
+
await updatePipelineRunStepStatus({
|
|
2040
|
+
member,
|
|
2041
|
+
runStepId: runStep.id,
|
|
2042
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
2043
|
+
error: errorMessage,
|
|
2044
|
+
});
|
|
2045
|
+
throw error;
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
// Execute in batches with concurrency limit
|
|
2050
|
+
const allOutputSubmissionIds: string[] = [];
|
|
2051
|
+
try {
|
|
2052
|
+
for (let i = 0; i < outputSubmissions.length; i += CONCURRENCY_LIMIT) {
|
|
2053
|
+
const batch = outputSubmissions.slice(i, i + CONCURRENCY_LIMIT);
|
|
2054
|
+
const batchPromises = batch.map((outputSubmission, batchIdx) =>
|
|
2055
|
+
executeSubmission(outputSubmission, i + batchIdx),
|
|
2056
|
+
);
|
|
2057
|
+
const batchResults = await Promise.all(batchPromises);
|
|
2058
|
+
allOutputSubmissionIds.push(...batchResults);
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
return {
|
|
2062
|
+
ok: true,
|
|
2063
|
+
data: { outputSubmissionIds: allOutputSubmissionIds, runSteps },
|
|
2064
|
+
};
|
|
2065
|
+
} catch (error) {
|
|
2066
|
+
const errorMessage =
|
|
2067
|
+
error instanceof Error ? error.message : "Step execution failed";
|
|
2068
|
+
return { ok: false, error: errorMessage };
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
// ============================================================================
|
|
2073
|
+
// Step Executor
|
|
2074
|
+
// ============================================================================
|
|
2075
|
+
|
|
2076
|
+
function buildStepResponseSchema(outputFormGroups: FormGroupHydrated[]) {
|
|
2077
|
+
const extractionSchema = outputFormGroups.reduce((acc, group) => {
|
|
2078
|
+
const groupSchema = buildExtractionSchemaFromGroup(group);
|
|
2079
|
+
return z.object({
|
|
2080
|
+
...acc.shape,
|
|
2081
|
+
...groupSchema.shape,
|
|
2082
|
+
});
|
|
2083
|
+
}, z.object({}));
|
|
2084
|
+
|
|
2085
|
+
return z.object({
|
|
2086
|
+
reasoning: z.string().nullable(),
|
|
2087
|
+
data: extractionSchema.nullable(),
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
async function runStepExecute({
|
|
2092
|
+
member,
|
|
2093
|
+
runId,
|
|
2094
|
+
runStepId,
|
|
2095
|
+
step,
|
|
2096
|
+
inputSubmissions,
|
|
2097
|
+
resourceSubmissions,
|
|
2098
|
+
outputFormGroups,
|
|
2099
|
+
conversationId,
|
|
2100
|
+
spawningGroupData,
|
|
2101
|
+
config,
|
|
2102
|
+
}: {
|
|
2103
|
+
member: Member;
|
|
2104
|
+
runId: string;
|
|
2105
|
+
runStepId: string;
|
|
2106
|
+
step: {
|
|
2107
|
+
id: string;
|
|
2108
|
+
stepOrder: number;
|
|
2109
|
+
description: string;
|
|
2110
|
+
prompt: string;
|
|
2111
|
+
toolsAllowed: string[] | null;
|
|
2112
|
+
parentStepId: string | null;
|
|
2113
|
+
outputFormIds: string[];
|
|
2114
|
+
};
|
|
2115
|
+
inputSubmissions: DenormSubmission[];
|
|
2116
|
+
resourceSubmissions: DenormSubmission[];
|
|
2117
|
+
outputFormGroups: FormGroupHydrated[];
|
|
2118
|
+
conversationId: string;
|
|
2119
|
+
spawningGroupData?: {
|
|
2120
|
+
groupName: string;
|
|
2121
|
+
data: DenormSubmission["groups"][0];
|
|
2122
|
+
} | null;
|
|
2123
|
+
config: {
|
|
2124
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
2125
|
+
};
|
|
2126
|
+
}): Promise<StepExecutionResult> {
|
|
2127
|
+
const { model } = config;
|
|
2128
|
+
try {
|
|
2129
|
+
// Serialize input data and resources
|
|
2130
|
+
// If multiple input submissions provided (parent was multi-step), serialize all of them
|
|
2131
|
+
const serializedInput = inputSubmissions
|
|
2132
|
+
.map(
|
|
2133
|
+
(sub, idx) => `=== Input ${idx + 1} ===\n${serializeSubmission(sub)}`,
|
|
2134
|
+
)
|
|
2135
|
+
.join("\n\n");
|
|
2136
|
+
const serializedResources = serializePipelineResources(resourceSubmissions);
|
|
2137
|
+
|
|
2138
|
+
// Build output schema
|
|
2139
|
+
const workflowStepResponseSchema =
|
|
2140
|
+
buildStepResponseSchema(outputFormGroups);
|
|
2141
|
+
const strictSchema = zodSchema(workflowStepResponseSchema);
|
|
2142
|
+
addAdditionalPropertiesToJsonSchema(strictSchema.jsonSchema as JSONSchema7);
|
|
2143
|
+
|
|
2144
|
+
// Build instructions (system context only)
|
|
2145
|
+
const instructionsParts = [
|
|
2146
|
+
"You are executing one step in a deterministic data workflow.",
|
|
2147
|
+
"Analyze the input data and produce the required output.",
|
|
2148
|
+
"",
|
|
2149
|
+
`Step: ${step.stepOrder}`,
|
|
2150
|
+
`Description: ${step.description}`,
|
|
2151
|
+
"",
|
|
2152
|
+
"IMPORTANT: For each primitive field (string, number, boolean), output:",
|
|
2153
|
+
' "fieldName": { "value": <extracted_value>, "refId": "<element_id>" }',
|
|
2154
|
+
"",
|
|
2155
|
+
"When a value comes from multiple elements in the input data or pipeline resources, list all element IDs separated by commas in the refId field.",
|
|
2156
|
+
"EVERY extracted value MUST include a refId pointing to the source element(s) from the input data or pipeline resources.",
|
|
2157
|
+
];
|
|
2158
|
+
|
|
2159
|
+
if (spawningGroupData) {
|
|
2160
|
+
instructionsParts.push(
|
|
2161
|
+
"",
|
|
2162
|
+
"NOTE: This is a spawned instance processing a specific row/entry from a multi-group table.",
|
|
2163
|
+
`The TARGET ROW section below contains the specific ${spawningGroupData.groupName} entry this instance is processing.`,
|
|
2164
|
+
"Use data from the TARGET ROW as the primary context, and reference INPUT DATA for additional context as needed.",
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
instructionsParts.push("", "===== STEP INSTRUCTIONS =====", step.prompt);
|
|
2169
|
+
|
|
2170
|
+
const instructions = instructionsParts.join("\n");
|
|
2171
|
+
|
|
2172
|
+
console.log("[runStepExecute] Building agent for step:", {
|
|
2173
|
+
runId,
|
|
2174
|
+
runStepId,
|
|
2175
|
+
stepOrder: step.stepOrder,
|
|
2176
|
+
stepDescription: step.description,
|
|
2177
|
+
promptLength: step.prompt.length,
|
|
2178
|
+
promptPreview: step.prompt.substring(0, 200),
|
|
2179
|
+
hasSpawningGroup: !!spawningGroupData,
|
|
2180
|
+
});
|
|
2181
|
+
|
|
2182
|
+
// Build messages with input data and resources
|
|
2183
|
+
const messages = [
|
|
2184
|
+
{
|
|
2185
|
+
role: "user" as const,
|
|
2186
|
+
content: `===== INPUT DATA =====\n${serializedInput}`,
|
|
2187
|
+
},
|
|
2188
|
+
];
|
|
2189
|
+
|
|
2190
|
+
if (spawningGroupData) {
|
|
2191
|
+
// Serialize the spawning group data
|
|
2192
|
+
const spawningGroupHtml: string[] = [];
|
|
2193
|
+
spawningGroupHtml.push(`<h3>${spawningGroupData.groupName}</h3>`);
|
|
2194
|
+
spawningGroupHtml.push("<dl>");
|
|
2195
|
+
|
|
2196
|
+
for (const item of spawningGroupData.data.items) {
|
|
2197
|
+
spawningGroupHtml.push(`<dt>${item.name}</dt>`);
|
|
2198
|
+
spawningGroupHtml.push(
|
|
2199
|
+
`<dd id="${item.id}">${String(item.value ?? "")}</dd>`,
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
for (const derived of spawningGroupData.data.derived) {
|
|
2204
|
+
spawningGroupHtml.push(`<dt>${derived.name} (derived)</dt>`);
|
|
2205
|
+
spawningGroupHtml.push(
|
|
2206
|
+
`<dd id="${derived.id}">${String(derived.value ?? "")}</dd>`,
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
spawningGroupHtml.push("</dl>");
|
|
2211
|
+
|
|
2212
|
+
messages.push({
|
|
2213
|
+
role: "user" as const,
|
|
2214
|
+
content: `===== TARGET ROW (Spawning Group) =====\n${spawningGroupHtml.join("\n")}`,
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
if (resourceSubmissions.length > 0) {
|
|
2219
|
+
messages.push({
|
|
2220
|
+
role: "user" as const,
|
|
2221
|
+
content: `===== PIPELINE RESOURCES =====\n${serializedResources}`,
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
messages.push({
|
|
2226
|
+
role: "user" as const,
|
|
2227
|
+
content: "Execute the step as instructed.",
|
|
2228
|
+
});
|
|
2229
|
+
|
|
2230
|
+
// Load pipeline-specific tools (not agent tools)
|
|
2231
|
+
const tools = {
|
|
2232
|
+
createArtifact: createPipelineArtifact({
|
|
2233
|
+
member,
|
|
2234
|
+
runId,
|
|
2235
|
+
runStepId,
|
|
2236
|
+
}),
|
|
2237
|
+
};
|
|
2238
|
+
|
|
2239
|
+
console.log("[runStepExecute] Tools available:", {
|
|
2240
|
+
runId,
|
|
2241
|
+
runStepId,
|
|
2242
|
+
toolNames: Object.keys(tools),
|
|
2243
|
+
toolCount: Object.keys(tools).length,
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
console.log("[runStepExecute] Full instructions being sent to agent:", {
|
|
2247
|
+
runId,
|
|
2248
|
+
runStepId,
|
|
2249
|
+
instructions,
|
|
2250
|
+
});
|
|
2251
|
+
|
|
2252
|
+
// Create agent with structured output
|
|
2253
|
+
const agent = new ToolLoopAgent({
|
|
2254
|
+
model,
|
|
2255
|
+
instructions,
|
|
2256
|
+
tools,
|
|
2257
|
+
stopWhen: stepCountIs(10),
|
|
2258
|
+
output: Output.object({
|
|
2259
|
+
schema: jsonSchema(strictSchema.jsonSchema),
|
|
2260
|
+
}),
|
|
2261
|
+
});
|
|
2262
|
+
|
|
2263
|
+
// Execute agent
|
|
2264
|
+
console.log("[runStepExecute] Starting agent execution...", {
|
|
2265
|
+
runId,
|
|
2266
|
+
runStepId,
|
|
2267
|
+
});
|
|
2268
|
+
|
|
2269
|
+
const result = await agent.generate({ messages });
|
|
2270
|
+
|
|
2271
|
+
console.log("[runStepExecute] Agent execution completed:", {
|
|
2272
|
+
runId,
|
|
2273
|
+
runStepId,
|
|
2274
|
+
hasOutput: !!result.output,
|
|
2275
|
+
stepCount: result.steps?.length ?? 0,
|
|
2276
|
+
});
|
|
2277
|
+
|
|
2278
|
+
const parsedOutput = workflowStepResponseSchema.parse(result.output);
|
|
2279
|
+
|
|
2280
|
+
return {
|
|
2281
|
+
status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
|
|
2282
|
+
reasoning: parsedOutput.reasoning ?? undefined,
|
|
2283
|
+
output: { data: parsedOutput.data },
|
|
2284
|
+
};
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
console.error("[runStepExecute] Step execution failed:", {
|
|
2287
|
+
runId,
|
|
2288
|
+
runStepId,
|
|
2289
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
2290
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
2291
|
+
});
|
|
2292
|
+
|
|
2293
|
+
return {
|
|
2294
|
+
status: PIPELINE_RUN_STATUS.FAILED,
|
|
2295
|
+
error: error instanceof Error ? error.message : "Step execution failed",
|
|
2296
|
+
output: { data: null },
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// ============================================================================
|
|
2302
|
+
// Replay Functions
|
|
2303
|
+
// ============================================================================
|
|
2304
|
+
|
|
2305
|
+
export async function replayPipelineRun({
|
|
2306
|
+
member,
|
|
2307
|
+
runId,
|
|
2308
|
+
config,
|
|
2309
|
+
}: {
|
|
2310
|
+
member: Member;
|
|
2311
|
+
runId: string;
|
|
2312
|
+
config: {
|
|
2313
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
2314
|
+
};
|
|
2315
|
+
}): Promise<DalResult<unknown>> {
|
|
2316
|
+
// TODO: Implement replay functionality
|
|
2317
|
+
// Steps needed:
|
|
2318
|
+
// 1. Get run timeline with getRunTimeline
|
|
2319
|
+
// 2. Extract input submission IDs from resources
|
|
2320
|
+
// 3. Call executePipeline with same inputs
|
|
2321
|
+
return { ok: false, error: "not-found" };
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
export async function replayPipelineRunStep({
|
|
2325
|
+
member,
|
|
2326
|
+
runStepId,
|
|
2327
|
+
config,
|
|
2328
|
+
}: {
|
|
2329
|
+
member: Member;
|
|
2330
|
+
runStepId: string;
|
|
2331
|
+
config: {
|
|
2332
|
+
model: ReturnType<typeof createLanguageModel>;
|
|
2333
|
+
};
|
|
2334
|
+
}): Promise<DalResult<unknown>> {
|
|
2335
|
+
// TODO: Implement step replay functionality
|
|
2336
|
+
// Steps needed:
|
|
2337
|
+
// 1. Get step details with getPipelineRunStepWithDetails
|
|
2338
|
+
// 2. Find the corresponding pipeline step
|
|
2339
|
+
// 3. Determine input (parent step output or run input)
|
|
2340
|
+
// 4. Either make executeSingleStep public or create wrapper
|
|
2341
|
+
return { ok: false, error: "not-found" };
|
|
2342
|
+
}
|