@startsimpli/integrations 0.2.14 → 0.2.17
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/package.json +2 -2
- package/src/__tests__/corpus-field-gate.test.ts +246 -0
- package/src/__tests__/corpus-llm-timeout.test.ts +91 -0
- package/src/__tests__/corpus-pages.test.tsx +47 -0
- package/src/__tests__/corpus-runs.test.ts +631 -0
- package/src/__tests__/corpus-tracker.test.tsx +254 -1
- package/src/components/pages/AskPage.tsx +11 -0
- package/src/components/pages/LibraryPage.tsx +12 -0
- package/src/components/pages/TrackerPage.tsx +326 -2
- package/src/corpus/ask.ts +21 -0
- package/src/corpus/client.ts +57 -1
- package/src/corpus/entities.ts +117 -1
- package/src/corpus/extract.ts +85 -8
- package/src/corpus/fixtures.ts +34 -0
- package/src/corpus/index.ts +29 -0
- package/src/corpus/llm.ts +32 -3
- package/src/corpus/runs.ts +563 -0
- package/src/corpus/types.ts +204 -2
package/src/corpus/types.ts
CHANGED
|
@@ -127,6 +127,19 @@ export interface SourceDocument {
|
|
|
127
127
|
scope: CorpusScope;
|
|
128
128
|
/** When the connector last wrote this row. Drives the freshness claim. */
|
|
129
129
|
ingestedAt?: string | null;
|
|
130
|
+
/**
|
|
131
|
+
* Attribute names the SERVER withheld from this reader
|
|
132
|
+
* (bd startsim-768w.38.33). The tenant's per-field read gate strips them from
|
|
133
|
+
* `data` and names them back in `redacted_fields`; this carries that fact all
|
|
134
|
+
* the way to the screen and into the prompt.
|
|
135
|
+
*
|
|
136
|
+
* IT HAS TO TRAVEL, and that is the whole reason it is on the document rather
|
|
137
|
+
* than swallowed at the mapper. A record whose transcript was withheld is NOT
|
|
138
|
+
* a record with no transcript. Anything that cannot tell the two apart — a
|
|
139
|
+
* reader, or a model asked "was this discussed?" — answers "no" with total
|
|
140
|
+
* confidence about text it was simply never shown.
|
|
141
|
+
*/
|
|
142
|
+
withheldFields?: string[];
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
/**
|
|
@@ -299,6 +312,14 @@ export interface AskResult {
|
|
|
299
312
|
* returned something it should not have, and is worth seeing.
|
|
300
313
|
*/
|
|
301
314
|
droppedOutOfScope: number;
|
|
315
|
+
/**
|
|
316
|
+
* Attribute names the server withheld from this reader on at least one record
|
|
317
|
+
* that was considered (bd startsim-768w.38.33). Reported for exactly the
|
|
318
|
+
* reason `droppedOutOfScope` is: an answer built from a partial record has to
|
|
319
|
+
* say so, or "your corpus has nothing about that" and "you were not shown the
|
|
320
|
+
* part that does" become the same sentence.
|
|
321
|
+
*/
|
|
322
|
+
withheldFields?: string[];
|
|
302
323
|
/** Which provider/model actually answered, and whether it was a real one. */
|
|
303
324
|
provider: LlmDescription;
|
|
304
325
|
/** Freshness at answer time — an answer over a stale corpus says so. */
|
|
@@ -323,6 +344,13 @@ export interface PromptDocument {
|
|
|
323
344
|
occurredAt: string | null;
|
|
324
345
|
participants: string[];
|
|
325
346
|
text: string;
|
|
347
|
+
/**
|
|
348
|
+
* Fields the reader was not allowed to see (bd startsim-768w.38.33). Rendered
|
|
349
|
+
* into the document block so the model states a limit instead of inventing a
|
|
350
|
+
* certainty: without it, a summary-only view of a meeting whose transcript was
|
|
351
|
+
* withheld reads as proof the thing was never discussed.
|
|
352
|
+
*/
|
|
353
|
+
withheldFields?: string[];
|
|
326
354
|
}
|
|
327
355
|
|
|
328
356
|
export interface AnswerRequest {
|
|
@@ -627,8 +655,14 @@ export interface ExtractionDraft {
|
|
|
627
655
|
*/
|
|
628
656
|
export interface CommitmentTrackerApi extends CommitmentApi {
|
|
629
657
|
/**
|
|
630
|
-
* Run
|
|
631
|
-
*
|
|
658
|
+
* Run ONE slice of extraction and wait for it.
|
|
659
|
+
*
|
|
660
|
+
* KEPT, but no longer what the button uses. It is the primitive a job is made
|
|
661
|
+
* of, and it is what a deployment small enough to finish inside a request can
|
|
662
|
+
* still call directly. Anything with a real corpus must use the pair below:
|
|
663
|
+
* one document is about twenty seconds of model time, so a seventeen-document
|
|
664
|
+
* sweep is six minutes and no slice size fits a browser request
|
|
665
|
+
* (bd startsim-768w.38.28).
|
|
632
666
|
*/
|
|
633
667
|
extract?(input: {
|
|
634
668
|
scope: CorpusScope;
|
|
@@ -637,11 +671,170 @@ export interface CommitmentTrackerApi extends CommitmentApi {
|
|
|
637
671
|
offset?: number;
|
|
638
672
|
limit?: number;
|
|
639
673
|
}): Promise<ExtractionResult>;
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Start — or resume — a sweep, and return WITHOUT doing the work.
|
|
677
|
+
*
|
|
678
|
+
* The composer polls `getExtractionRun` for progress. Starting must be cheap
|
|
679
|
+
* and must return a durable run, because the whole point is that the thing
|
|
680
|
+
* doing the work outlives the request that asked for it.
|
|
681
|
+
*/
|
|
682
|
+
startExtraction?(input: {
|
|
683
|
+
scope: CorpusScope;
|
|
684
|
+
/** Ignore a resumable sweep and begin again from the first document. */
|
|
685
|
+
restart?: boolean;
|
|
686
|
+
}): Promise<ExtractionRun>;
|
|
687
|
+
|
|
688
|
+
/** The current sweep, from the server's own record of it. */
|
|
689
|
+
getExtractionRun?(): Promise<ExtractionRun | null>;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* WHY THE MODEL HALF DID NOT RUN.
|
|
694
|
+
*
|
|
695
|
+
* This type exists because of a specific lie. `extractCommitments` deliberately
|
|
696
|
+
* wraps the model call so that a provider outage costs the extras and not the
|
|
697
|
+
* list — which is right — but the old code turned every such failure into
|
|
698
|
+
* `fromModel: 0` and nothing else, and `fromModel: 0` reads exactly like "the
|
|
699
|
+
* model found nothing to add". On the live tandem tenant that meant a week of
|
|
700
|
+
* rules-only runs being reported as model runs, with a blown sixty-second client
|
|
701
|
+
* timeout as the invisible cause. A degraded run is fine. A degraded run that
|
|
702
|
+
* presents itself as a complete one is not.
|
|
703
|
+
*/
|
|
704
|
+
export interface ExtractionModelFailure {
|
|
705
|
+
/** Which model call failed — proposing items, or checking them. */
|
|
706
|
+
stage: 'extract' | 'verify';
|
|
707
|
+
message: string;
|
|
708
|
+
/**
|
|
709
|
+
* Whether the provider client's own deadline was hit, as opposed to a 401, a
|
|
710
|
+
* 429 or a bug. Worth separating because it is the only one the operator fixes
|
|
711
|
+
* by giving the run more time rather than by fixing a credential.
|
|
712
|
+
*/
|
|
713
|
+
timedOut: boolean;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
export type ExtractionRunStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
|
|
717
|
+
|
|
718
|
+
/** Everything a sweep has accumulated so far, summed across its slices. */
|
|
719
|
+
export interface ExtractionRunTotals {
|
|
720
|
+
documentsProcessed: number;
|
|
721
|
+
fromRules: number;
|
|
722
|
+
fromModel: number;
|
|
723
|
+
ungrounded: number;
|
|
724
|
+
unconfirmed: number;
|
|
725
|
+
created: number;
|
|
726
|
+
merged: number;
|
|
727
|
+
resolved: number;
|
|
728
|
+
droppedOutOfScope: number;
|
|
729
|
+
/** Slices whose model half answered. */
|
|
730
|
+
modelCalls: number;
|
|
731
|
+
/** Slices whose model half did NOT answer. The number that used to be hidden. */
|
|
732
|
+
modelFailures: number;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/** A finished sweep, kept so the screen can say what the last one did. */
|
|
736
|
+
export interface ExtractionRunSummary {
|
|
737
|
+
runId: string;
|
|
738
|
+
status: ExtractionRunStatus;
|
|
739
|
+
startedAt: string;
|
|
740
|
+
finishedAt: string | null;
|
|
741
|
+
totals: ExtractionRunTotals;
|
|
742
|
+
error: string | null;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* A SWEEP, as a durable record rather than as a request.
|
|
747
|
+
*
|
|
748
|
+
* Stored in the tenant's own generic entity store, which is what makes every
|
|
749
|
+
* property this design owes true at once: partial progress survives the process
|
|
750
|
+
* that made it, a poll from any replica sees the same run, and a driver that
|
|
751
|
+
* dies leaves a cursor rather than a hole. Nothing about it is Tandem-specific
|
|
752
|
+
* or meeting-specific — it is "a long, resumable, idempotent pass over the
|
|
753
|
+
* corpus", which is what every foundry's connector framework eventually needs.
|
|
754
|
+
*/
|
|
755
|
+
export interface ExtractionRun {
|
|
756
|
+
/** Identifies this SWEEP. The row it lives in is a singleton per workspace. */
|
|
757
|
+
runId: string;
|
|
758
|
+
/**
|
|
759
|
+
* The workspace the sweep is over.
|
|
760
|
+
*
|
|
761
|
+
* Stored, because a background driver has no request to read it from once the
|
|
762
|
+
* request that started it has returned — that is the whole point of the
|
|
763
|
+
* record. It cannot be forged: the row lives in the tenant entity store, which
|
|
764
|
+
* only ever returns rows in the caller's own org, and every slice re-runs
|
|
765
|
+
* `enforceScope` over the documents it read anyway. Adopting a stalled sweep
|
|
766
|
+
* overwrites it with the adopter's freshly proven scope.
|
|
767
|
+
*/
|
|
768
|
+
scope: CorpusScope;
|
|
769
|
+
status: ExtractionRunStatus;
|
|
770
|
+
/**
|
|
771
|
+
* The next document offset to process. THE resumption mechanism: a sweep
|
|
772
|
+
* interrupted at nine of seventeen restarts at ten, not at one.
|
|
773
|
+
*/
|
|
774
|
+
cursor: number;
|
|
775
|
+
/** In-scope documents at the last slice, so progress is a fraction of something. */
|
|
776
|
+
totalDocuments: number;
|
|
777
|
+
totals: ExtractionRunTotals;
|
|
778
|
+
/**
|
|
779
|
+
* Documents this sweep has already handed to the extractor, bounded.
|
|
780
|
+
*
|
|
781
|
+
* The cursor is an offset into a list whose order can shift when a document
|
|
782
|
+
* arrives mid-sweep. Recording the ids is what lets the run notice that it
|
|
783
|
+
* finished with fewer documents read than the corpus holds and SAY so, rather
|
|
784
|
+
* than reporting a complete sweep that quietly skipped one.
|
|
785
|
+
*/
|
|
786
|
+
processedIds: string[];
|
|
787
|
+
/** Documents that got the deterministic pass only, because the model did not answer. */
|
|
788
|
+
modelIncompleteIds: string[];
|
|
789
|
+
needsReview: Array<{ commitmentId: string; documentId: string; quote: string }>;
|
|
790
|
+
provider: LlmDescription;
|
|
791
|
+
startedAt: string;
|
|
792
|
+
updatedAt: string;
|
|
793
|
+
finishedAt: string | null;
|
|
794
|
+
/**
|
|
795
|
+
* WHO IS DRIVING, AND UNTIL WHEN.
|
|
796
|
+
*
|
|
797
|
+
* Not an optimisation. Two drivers over one cursor means two model calls over
|
|
798
|
+
* the same document, and two model calls over one document produce two
|
|
799
|
+
* differently-worded items that identity resolution cannot see are the same —
|
|
800
|
+
* which is precisely how a live re-run once turned 82 commitments into 98.
|
|
801
|
+
*/
|
|
802
|
+
leaseOwner: string | null;
|
|
803
|
+
leaseUntil: string | null;
|
|
804
|
+
/** Why the sweep stopped, when it stopped badly. */
|
|
805
|
+
error: string | null;
|
|
806
|
+
lastModelFailure: ExtractionModelFailure | null;
|
|
807
|
+
/** Previous sweeps, newest first, bounded. */
|
|
808
|
+
history: ExtractionRunSummary[];
|
|
809
|
+
/** True when this start adopted a sweep whose driver had gone away. Transient. */
|
|
810
|
+
adopted?: boolean;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Where a run record lives. ONE ROW PER WORKSPACE, rewritten in place.
|
|
815
|
+
*
|
|
816
|
+
* A row per sweep would be more history, and it would also be unbounded growth
|
|
817
|
+
* in the tenant's own corpus plus a scan on every poll — the tenant entity API
|
|
818
|
+
* filters on `?type=` and nothing else, so "the newest run" cannot be asked for
|
|
819
|
+
* directly. A singleton keyed on a fixed `external_id` is one deterministic
|
|
820
|
+
* read, never grows, and keeps the last few sweeps inline where the screen
|
|
821
|
+
* actually wants them.
|
|
822
|
+
*/
|
|
823
|
+
export interface ExtractionRunStore {
|
|
824
|
+
readRun(): Promise<ExtractionRun | null>;
|
|
825
|
+
writeRun(run: ExtractionRun): Promise<ExtractionRun>;
|
|
640
826
|
}
|
|
641
827
|
|
|
642
828
|
export interface ExtractionResult {
|
|
643
829
|
/** Documents read and sent to the extractor IN THIS CALL. */
|
|
644
830
|
documentsProcessed: number;
|
|
831
|
+
/**
|
|
832
|
+
* Which ones, by id. A sweep needs this to record what it has really covered
|
|
833
|
+
* — an offset cursor alone cannot tell a resumed run whether the list moved
|
|
834
|
+
* under it — and the screen needs it to name the meetings a degraded slice
|
|
835
|
+
* only half-read.
|
|
836
|
+
*/
|
|
837
|
+
documentIds: string[];
|
|
645
838
|
/** Documents in scope altogether, so a partial sweep can say what it is of. */
|
|
646
839
|
totalDocuments: number;
|
|
647
840
|
/**
|
|
@@ -684,6 +877,15 @@ export interface ExtractionResult {
|
|
|
684
877
|
needsReview: Array<{ commitmentId: string; documentId: string; quote: string }>;
|
|
685
878
|
droppedOutOfScope: number;
|
|
686
879
|
provider: LlmDescription;
|
|
880
|
+
/**
|
|
881
|
+
* `null` when the model half ran. Populated when it did not, WITH THE REASON.
|
|
882
|
+
*
|
|
883
|
+
* Read this before reading `fromModel`. Zero findings and a failed call are
|
|
884
|
+
* different facts and only one of them is about the corpus; conflating them is
|
|
885
|
+
* what let a rules-only run be reported as a model run for a week
|
|
886
|
+
* (bd startsim-768w.38.28).
|
|
887
|
+
*/
|
|
888
|
+
modelFailure: ExtractionModelFailure | null;
|
|
687
889
|
}
|
|
688
890
|
|
|
689
891
|
/**
|