@company-semantics/contracts 98.0.0 → 99.1.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/package.json +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +51 -2
- package/src/chat/__tests__/runtime-profile.test.ts +23 -5
- package/src/generated/openapi-routes.ts +1 -0
- package/src/index.ts +4 -1
- package/src/ingestion/README.md +51 -21
- package/src/ingestion/__tests__/providers.test.ts +59 -0
- package/src/ingestion/__tests__/registry.test.ts +18 -18
- package/src/ingestion/__tests__/surfaces.test.ts +68 -40
- package/src/ingestion/index.ts +20 -2
- package/src/ingestion/providers.ts +141 -0
- package/src/ingestion/registry.ts +44 -25
- package/src/ingestion/surfaces.ts +81 -28
- package/src/ingestion/types.ts +80 -15
- package/src/schedules/README.md +118 -0
- package/src/schedules/__tests__/README.md +27 -0
- package/src/schedules/__tests__/api.test.ts +72 -0
- package/src/schedules/__tests__/cron.test.ts +140 -0
- package/src/schedules/__tests__/definition.test.ts +97 -0
- package/src/schedules/__tests__/execution.test.ts +75 -0
- package/src/schedules/__tests__/identity.test.ts +51 -0
- package/src/schedules/__tests__/schedules-exports.test.ts +26 -0
- package/src/schedules/api.ts +139 -0
- package/src/schedules/cron.ts +268 -0
- package/src/schedules/definition.ts +148 -0
- package/src/schedules/execution.ts +146 -0
- package/src/schedules/identity.ts +99 -0
- package/src/schedules/index.ts +124 -0
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '935ee995d53d' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '935ee995d53d56538f0566bb3c1372041d5cb1def41f3b28db02716ad4d8c591' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -421,6 +421,23 @@ export interface paths {
|
|
|
421
421
|
patch?: never;
|
|
422
422
|
trace?: never;
|
|
423
423
|
};
|
|
424
|
+
"/api/chats/{id}/branch": {
|
|
425
|
+
parameters: {
|
|
426
|
+
query?: never;
|
|
427
|
+
header?: never;
|
|
428
|
+
path?: never;
|
|
429
|
+
cookie?: never;
|
|
430
|
+
};
|
|
431
|
+
get?: never;
|
|
432
|
+
put?: never;
|
|
433
|
+
/** Make one version of a turn the active branch */
|
|
434
|
+
post: operations["selectChatBranch"];
|
|
435
|
+
delete?: never;
|
|
436
|
+
options?: never;
|
|
437
|
+
head?: never;
|
|
438
|
+
patch?: never;
|
|
439
|
+
trace?: never;
|
|
440
|
+
};
|
|
424
441
|
"/api/chats/{id}/pin": {
|
|
425
442
|
parameters: {
|
|
426
443
|
query?: never;
|
|
@@ -5719,6 +5736,10 @@ export interface components {
|
|
|
5719
5736
|
title: string;
|
|
5720
5737
|
isAutoGenerated: boolean;
|
|
5721
5738
|
};
|
|
5739
|
+
ChatBranchSelectRequest: {
|
|
5740
|
+
/** Format: uuid */
|
|
5741
|
+
messageId: string;
|
|
5742
|
+
};
|
|
5722
5743
|
ChatSseEvent: {
|
|
5723
5744
|
/** @constant */
|
|
5724
5745
|
v: 1;
|
|
@@ -5987,14 +6008,16 @@ export interface components {
|
|
|
5987
6008
|
ChatMessageFeedbackResponse: {
|
|
5988
6009
|
/** @enum {string} */
|
|
5989
6010
|
rating: "up" | "down";
|
|
5990
|
-
reason:
|
|
6011
|
+
reason: ("incorrect" | "didnt_follow_instructions" | "not_helpful" | "missing_context" | "tool_or_workflow_issue" | "other") | null;
|
|
5991
6012
|
/** Format: date-time */
|
|
5992
6013
|
updatedAt: string;
|
|
5993
6014
|
};
|
|
5994
6015
|
PutChatMessageFeedbackRequest: {
|
|
5995
6016
|
/** @enum {string} */
|
|
5996
6017
|
rating: "up" | "down";
|
|
5997
|
-
|
|
6018
|
+
/** @enum {string} */
|
|
6019
|
+
reason?: "incorrect" | "didnt_follow_instructions" | "not_helpful" | "missing_context" | "tool_or_workflow_issue" | "other";
|
|
6020
|
+
comment?: string;
|
|
5998
6021
|
};
|
|
5999
6022
|
WorkspaceAccessResponse: {
|
|
6000
6023
|
hasAccess: boolean;
|
|
@@ -12463,6 +12486,32 @@ export interface operations {
|
|
|
12463
12486
|
};
|
|
12464
12487
|
};
|
|
12465
12488
|
};
|
|
12489
|
+
selectChatBranch: {
|
|
12490
|
+
parameters: {
|
|
12491
|
+
query?: never;
|
|
12492
|
+
header?: never;
|
|
12493
|
+
path: {
|
|
12494
|
+
id: string;
|
|
12495
|
+
};
|
|
12496
|
+
cookie?: never;
|
|
12497
|
+
};
|
|
12498
|
+
requestBody: {
|
|
12499
|
+
content: {
|
|
12500
|
+
"application/json": components["schemas"]["ChatBranchSelectRequest"];
|
|
12501
|
+
};
|
|
12502
|
+
};
|
|
12503
|
+
responses: {
|
|
12504
|
+
/** @description The chat with its new active branch */
|
|
12505
|
+
200: {
|
|
12506
|
+
headers: {
|
|
12507
|
+
[name: string]: unknown;
|
|
12508
|
+
};
|
|
12509
|
+
content: {
|
|
12510
|
+
"application/json": components["schemas"]["GetChatResponse"];
|
|
12511
|
+
};
|
|
12512
|
+
};
|
|
12513
|
+
};
|
|
12514
|
+
};
|
|
12466
12515
|
pinChat: {
|
|
12467
12516
|
parameters: {
|
|
12468
12517
|
query?: never;
|
|
@@ -81,13 +81,31 @@ function backendAgenticModelDefault(): string {
|
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* The chat model the factory declares to the provider resolver. The factory's
|
|
86
|
+
* CHAT_DEFAULT_MODEL reads `modelFor('chat.default')`, so the id lives on the
|
|
87
|
+
* `chat.default` entry of the backend's model use-site registry.
|
|
88
|
+
*
|
|
89
|
+
* Until backend PRD-01163 lands on its origin/main, the resolved backend (a
|
|
90
|
+
* detached checkout such as the PR gate reads origin/main) has no registry and
|
|
91
|
+
* still declares the literal in factory.ts. Read whichever declaration the
|
|
92
|
+
* resolved backend actually has — parity is asserted either way. Drop the
|
|
93
|
+
* factory.ts branch once the registry is on backend main.
|
|
94
|
+
*/
|
|
85
95
|
function backendChatModelDefault(): string {
|
|
96
|
+
if (!backend!.exists("src/runtimes/models/use-sites.ts")) {
|
|
97
|
+
return extractDeclaration(
|
|
98
|
+
readBackendSource("src", "chat", "orchestrator", "factory.ts"),
|
|
99
|
+
/CHAT_DEFAULT_MODEL\s*=\s*['"]([^'"]+)['"]/,
|
|
100
|
+
"CHAT_DEFAULT_MODEL",
|
|
101
|
+
"src/chat/orchestrator/factory.ts",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
86
104
|
return extractDeclaration(
|
|
87
|
-
readBackendSource("src", "
|
|
88
|
-
/
|
|
89
|
-
"
|
|
90
|
-
"src/
|
|
105
|
+
readBackendSource("src", "runtimes", "models", "use-sites.ts"),
|
|
106
|
+
/['"]chat\.default['"]\s*:\s*\{\s*modelId:\s*['"]([^'"]+)['"]/,
|
|
107
|
+
"the 'chat.default' use-site modelId",
|
|
108
|
+
"src/runtimes/models/use-sites.ts",
|
|
91
109
|
);
|
|
92
110
|
}
|
|
93
111
|
|
|
@@ -28,6 +28,7 @@ export const openApiRoutes = {
|
|
|
28
28
|
'/api/chats/proactive/{eventId}': ['GET'],
|
|
29
29
|
'/api/chats/resolve-launch': ['POST'],
|
|
30
30
|
'/api/chats/{id}': ['DELETE', 'GET', 'PATCH'],
|
|
31
|
+
'/api/chats/{id}/branch': ['POST'],
|
|
31
32
|
'/api/chats/{id}/generate-title': ['POST'],
|
|
32
33
|
'/api/chats/{id}/messages': ['POST'],
|
|
33
34
|
'/api/chats/{id}/messages/{messageId}': ['DELETE'],
|
package/src/index.ts
CHANGED
|
@@ -324,7 +324,10 @@ export * from "./product-intake/index";
|
|
|
324
324
|
// for one target document; the wire carries facts and a count, never content.
|
|
325
325
|
// See ./source-sync/vocabulary.ts and ./source-sync/schemas.ts.
|
|
326
326
|
export * from "./source-sync/index";
|
|
327
|
-
|
|
328
327
|
// Quality control plane vocabulary (PRD-01088 / quality-control-plane)
|
|
329
328
|
// Closed evaluation/decision sets plus QualityRunReportSchema — ids, scores and failure kinds only. See ./quality/index.ts.
|
|
330
329
|
export * from "./quality/index";
|
|
330
|
+
// Scheduled-execution vocabulary (PRD-01175 / schedule-execution-wire-types)
|
|
331
|
+
// Schedule definitions, slot-derived execution identity, lifecycle, the pure
|
|
332
|
+
// UTC cron evaluator and the schedule route responses. See ./schedules/README.md.
|
|
333
|
+
export * from "./schedules";
|
package/src/ingestion/README.md
CHANGED
|
@@ -1,49 +1,79 @@
|
|
|
1
1
|
# ingestion/
|
|
2
2
|
|
|
3
|
-
Shared vocabulary for the
|
|
3
|
+
Shared vocabulary for the one ingestion pipeline: formats, surfaces, providers.
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
7
|
-
The single source of truth for which
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
The single source of truth for which files the platform ingests and what each
|
|
8
|
+
surface does with them. The format registry names every accepted format — its
|
|
9
|
+
MIME type, filename extensions, family, class, human label group and byte cap.
|
|
10
|
+
The surface declarations say, per consuming surface (company-md, org-chart,
|
|
11
|
+
chat, profile-image), which treatment each admitted family gets and the byte
|
|
12
|
+
ceiling. The provider vocabulary names the native types an external source
|
|
13
|
+
(Google Drive) hands the pipeline and the registry format each exports to. The
|
|
14
|
+
backend normalizer, discovery and the app upload UI derive everything they need
|
|
15
|
+
from these three declarations.
|
|
12
16
|
|
|
13
17
|
## Invariants
|
|
14
18
|
|
|
15
19
|
- `INGESTION_FORMATS` is the canonical list; `mime` is unique across entries.
|
|
16
|
-
|
|
20
|
+
Entries are ordered text → documents → images/diagrams → audio/video; the
|
|
17
21
|
backend's MIME-order test and the derived UI strings depend on this order.
|
|
18
|
-
-
|
|
19
|
-
|
|
22
|
+
- Every format carries a `family` and a required `class` (`FormatClass`:
|
|
23
|
+
`docs | slides | sheets | pdf | text | image | diagram | audio | video`). No
|
|
24
|
+
consumer keeps its own extension → class table.
|
|
25
|
+
- A surface (`INGESTION_SURFACES`) declares `treatments` — family →
|
|
26
|
+
`text | vision | transcript | store` — and a required `maxBytes`.
|
|
27
|
+
`familiesOf`, `formatsFor`, `surfaceAdmits`, `treatmentFor`, `acceptFor`,
|
|
28
|
+
`supportedLabelFor` and `maxBytesFor` are **derived** from that declaration.
|
|
29
|
+
Treatments are per family, never per format.
|
|
30
|
+
- `vision` means the richest native form: images as images, a PDF as a binary
|
|
31
|
+
document, text-native formats as text.
|
|
32
|
+
- A provider (`INGESTION_PROVIDERS`) declares its native types, their export
|
|
33
|
+
targets and labels, and its container MIMEs. `providerFormatsFor`,
|
|
34
|
+
`classOfMime`, `inventoryClassesFor` and `labelOfMime` are **derived**.
|
|
35
|
+
- `acceptFor` emits extensions and MIMEs (".png,image/png,…"), so a
|
|
36
|
+
MIME-only matcher can use the same string as a file-input `accept`.
|
|
37
|
+
- Nothing outside this domain names a MIME, an extension or a provider-native
|
|
38
|
+
type; consumers ask these helpers (ADR slug `single-ingestion-pipeline`).
|
|
20
39
|
- No normalizer-routing fields here (`canonicalKind`, `normalizerKey` are
|
|
21
|
-
backend-only). This package stays pure vocabulary
|
|
22
|
-
|
|
23
|
-
each upload surface admits, and `formatsFor` / `acceptFor` /
|
|
24
|
-
`supportedLabelFor` / `maxBytesFor` are **derived** from that declaration
|
|
25
|
-
(ADR-CONTRACTS-159). A surface never re-spells a MIME.
|
|
40
|
+
backend-only). This package stays pure vocabulary
|
|
41
|
+
(ADR slug `ingestion-surface-treatments-and-providers`).
|
|
26
42
|
|
|
27
43
|
<!-- BEGIN GENERATED: readme-public-api — derived from code by `pnpm readme-api`. Do not edit. -->
|
|
28
44
|
|
|
29
45
|
## Public API
|
|
30
46
|
|
|
47
|
+
- `FORMAT_CLASSES` — The coarse, provider-agnostic kind of a format, used wherever the system classes a file without reading it…
|
|
48
|
+
- `FormatClass` _(type)_
|
|
31
49
|
- `INGESTION_FORMATS` — The canonical public format registry.
|
|
32
|
-
- `
|
|
33
|
-
- `
|
|
50
|
+
- `INGESTION_PROVIDERS` — Every provider's native types and container MIMEs, keyed by provider.
|
|
51
|
+
- `INGESTION_SURFACES` — The canonical surface declarations. - `company-md`: context bank, goals extraction, Source Sync landing.
|
|
34
52
|
- `IngestionFamily` _(type)_ — The semantic family a format belongs to.
|
|
35
53
|
- `IngestionFormat` _(type)_ — One accepted upload format, keyed by its canonical MIME type.
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
38
|
-
- `
|
|
54
|
+
- `IngestionProvider` _(type)_ — One provider: its native types and the structural container MIMEs it lists.
|
|
55
|
+
- `IngestionProviderKey` _(type)_ — External sources that hand the pipeline provider-native files.
|
|
56
|
+
- `IngestionSurface` _(type)_ — One surface: the treatment it applies per admitted family, and its byte ceiling.
|
|
57
|
+
- `IngestionSurfaceKey` _(type)_ — The surfaces that consume the registry.
|
|
58
|
+
- `InventoryClass` _(type)_ — A Source Sync inventory class: a format class, or a provider shortcut row.
|
|
59
|
+
- `ProviderFormat` _(type)_ — One MIME a provider can hand the pipeline, with the registry format it lands as.
|
|
60
|
+
- `ProviderNativeFormat` _(type)_ — A provider-native type that is exported to a registry format before ingestion.
|
|
61
|
+
- `TREATMENTS` — What a surface does with an admitted family.
|
|
62
|
+
- `Treatment` _(type)_
|
|
39
63
|
- `acceptExtensions` — All accepted filename extensions, in registry order.
|
|
40
|
-
- `acceptFor` —
|
|
64
|
+
- `acceptFor` — The `<input accept>` value for a surface: per format, its extensions then its MIME…
|
|
41
65
|
- `byMime` — Look up the entry for a canonical MIME; undefined means unsupported.
|
|
66
|
+
- `classOfMime` — The class of a provider-listed MIME; null for containers and unknown types.
|
|
67
|
+
- `familiesOf` — The families a surface admits, in canonical family order.
|
|
42
68
|
- `formatsFor` — The registry formats a surface admits, in registry order.
|
|
43
69
|
- `humanLabels` — Unique group labels for human display, in registry order.
|
|
44
|
-
- `
|
|
70
|
+
- `inventoryClassesFor` — The inventory classes discovery can record for a provider feeding a surface, in canonical class order, plus…
|
|
71
|
+
- `labelOfMime` — Display label for a MIME (provider-native first when a provider is given); null if unknown.
|
|
72
|
+
- `maxBytesFor` — The transport byte ceiling: the largest registry cap among admitted formats, tightened to the (required)…
|
|
73
|
+
- `providerFormatsFor` — Every MIME the provider can hand a surface (natives first, then registry order).
|
|
45
74
|
- `supportedLabelFor` — "Supported: …" copy naming every label group the surface admits.
|
|
46
75
|
- `surfaceAdmits` — True when the surface admits this registry format.
|
|
76
|
+
- `treatmentFor` — What the surface does with this format; null = the surface refuses it.
|
|
47
77
|
|
|
48
78
|
<!-- END GENERATED: readme-public-api -->
|
|
49
79
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
INGESTION_PROVIDERS,
|
|
4
|
+
providerFormatsFor,
|
|
5
|
+
classOfMime,
|
|
6
|
+
inventoryClassesFor,
|
|
7
|
+
labelOfMime,
|
|
8
|
+
} from "../providers";
|
|
9
|
+
import { byMime } from "../registry";
|
|
10
|
+
|
|
11
|
+
describe("ingestion providers", () => {
|
|
12
|
+
it("every Drive native type exports to a registry format", () => {
|
|
13
|
+
for (const n of INGESTION_PROVIDERS["google-drive"].nativeFormats) {
|
|
14
|
+
expect(byMime(n.exportsTo)).toBeDefined();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("the company-md Drive net is the three Workspace types plus the company-md formats", () => {
|
|
19
|
+
const net = providerFormatsFor("google-drive", "company-md");
|
|
20
|
+
expect(net.slice(0, 3).map((pf) => pf.mime)).toEqual([
|
|
21
|
+
"application/vnd.google-apps.document",
|
|
22
|
+
"application/vnd.google-apps.presentation",
|
|
23
|
+
"application/vnd.google-apps.spreadsheet",
|
|
24
|
+
]);
|
|
25
|
+
expect(net.map((pf) => pf.mime)).toContain("application/pdf");
|
|
26
|
+
expect(net.map((pf) => pf.mime)).not.toContain("image/png");
|
|
27
|
+
expect(new Set(net.map((pf) => pf.mime)).size).toBe(net.length);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("pins the company-md inventory classes the DB CHECK carries today", () => {
|
|
31
|
+
expect(inventoryClassesFor("google-drive", "company-md")).toEqual([
|
|
32
|
+
"docs",
|
|
33
|
+
"slides",
|
|
34
|
+
"sheets",
|
|
35
|
+
"pdf",
|
|
36
|
+
"text",
|
|
37
|
+
"shortcut",
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("classes native, downloaded and container MIMEs", () => {
|
|
42
|
+
expect(
|
|
43
|
+
classOfMime("google-drive", "application/vnd.google-apps.spreadsheet"),
|
|
44
|
+
).toBe("sheets");
|
|
45
|
+
expect(classOfMime("google-drive", "text/csv")).toBe("sheets");
|
|
46
|
+
expect(classOfMime("google-drive", "image/png")).toBe("image");
|
|
47
|
+
expect(
|
|
48
|
+
classOfMime("google-drive", "application/vnd.google-apps.folder"),
|
|
49
|
+
).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("labels native and registry MIMEs", () => {
|
|
53
|
+
expect(
|
|
54
|
+
labelOfMime("application/vnd.google-apps.document", "google-drive"),
|
|
55
|
+
).toBe("Google Doc");
|
|
56
|
+
expect(labelOfMime("application/pdf")).toBe("PDF");
|
|
57
|
+
expect(labelOfMime("application/x-unknown")).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
byMime,
|
|
5
|
-
acceptExtensions,
|
|
6
|
-
humanLabels,
|
|
7
|
-
INGEST_ACCEPT,
|
|
8
|
-
SUPPORTED_LABEL,
|
|
9
|
-
} from "../registry";
|
|
2
|
+
import { INGESTION_FORMATS, byMime, acceptExtensions } from "../registry";
|
|
3
|
+
import { FORMAT_CLASSES } from "../types";
|
|
10
4
|
|
|
11
5
|
describe("ingestion registry", () => {
|
|
12
6
|
it("has a unique MIME per entry", () => {
|
|
@@ -28,17 +22,23 @@ describe("ingestion registry", () => {
|
|
|
28
22
|
expect(byMime("application/x-unknown")).toBeUndefined();
|
|
29
23
|
});
|
|
30
24
|
|
|
31
|
-
it("
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf,.png,.jpg,.jpeg,.webp,.gif,.heic,.svg,.drawio,.mp3,.m4a,.wav,.aac,.mp4,.m4v,.mov",
|
|
35
|
-
);
|
|
25
|
+
it("every format carries a known class", () => {
|
|
26
|
+
for (const entry of INGESTION_FORMATS)
|
|
27
|
+
expect(FORMAT_CLASSES).toContain(entry.class);
|
|
36
28
|
});
|
|
37
29
|
|
|
38
|
-
it("
|
|
39
|
-
expect(
|
|
40
|
-
expect(
|
|
41
|
-
|
|
42
|
-
);
|
|
30
|
+
it("classes documents the way discovery does", () => {
|
|
31
|
+
expect(byMime("text/csv")!.class).toBe("sheets");
|
|
32
|
+
expect(byMime("application/epub+zip")!.class).toBe("docs");
|
|
33
|
+
expect(byMime("application/vnd.ms-powerpoint")!.class).toBe("slides");
|
|
34
|
+
expect(byMime("application/pdf")!.class).toBe("pdf");
|
|
35
|
+
expect(byMime("text/markdown")!.class).toBe("text");
|
|
36
|
+
expect(byMime("image/svg+xml")!.class).toBe("diagram");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("admits HEIF and no longer lists drawio", () => {
|
|
40
|
+
expect(byMime("image/heif")?.family).toBe("image");
|
|
41
|
+
expect(byMime("application/vnd.jgraph.mxfile")).toBeUndefined();
|
|
42
|
+
expect(acceptExtensions()).not.toContain(".drawio");
|
|
43
43
|
});
|
|
44
44
|
});
|
|
@@ -1,64 +1,92 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { byMime } from "../registry";
|
|
3
|
+
import { TREATMENTS } from "../types";
|
|
4
|
+
import type { IngestionSurfaceKey } from "../types";
|
|
3
5
|
import {
|
|
4
6
|
INGESTION_SURFACES,
|
|
7
|
+
familiesOf,
|
|
8
|
+
treatmentFor,
|
|
5
9
|
formatsFor,
|
|
6
|
-
surfaceAdmits,
|
|
7
10
|
acceptFor,
|
|
8
|
-
supportedLabelFor,
|
|
9
11
|
maxBytesFor,
|
|
10
12
|
} from "../surfaces";
|
|
11
13
|
|
|
12
14
|
describe("ingestion surfaces", () => {
|
|
13
|
-
it("every
|
|
14
|
-
for (const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
it("every surface declares treatments only for real families and a positive ceiling", () => {
|
|
16
|
+
for (const key of Object.keys(
|
|
17
|
+
INGESTION_SURFACES,
|
|
18
|
+
) as IngestionSurfaceKey[]) {
|
|
19
|
+
const s = INGESTION_SURFACES[key];
|
|
20
|
+
expect(s.key).toBe(key);
|
|
21
|
+
expect(s.maxBytes).toBeGreaterThan(0);
|
|
22
|
+
for (const [family, treatment] of Object.entries(s.treatments)) {
|
|
23
|
+
expect([
|
|
24
|
+
"text",
|
|
25
|
+
"document",
|
|
26
|
+
"image",
|
|
27
|
+
"diagram",
|
|
28
|
+
"audio",
|
|
29
|
+
"video",
|
|
30
|
+
]).toContain(family);
|
|
31
|
+
expect(TREATMENTS).toContain(treatment);
|
|
32
|
+
}
|
|
33
|
+
expect(familiesOf(key).length).toBeGreaterThan(0);
|
|
18
34
|
}
|
|
19
35
|
});
|
|
20
36
|
|
|
21
|
-
it("
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const order = formats.map((f) => INGESTION_FORMATS.indexOf(f));
|
|
28
|
-
expect(order).toEqual([...order].sort((a, b) => a - b));
|
|
29
|
-
}
|
|
37
|
+
it("company-md turns text and documents into text and refuses images and media", () => {
|
|
38
|
+
expect(treatmentFor("company-md", byMime("application/pdf")!)).toBe("text");
|
|
39
|
+
expect(treatmentFor("company-md", byMime("text/csv")!)).toBe("text");
|
|
40
|
+
expect(treatmentFor("company-md", byMime("image/png")!)).toBeNull();
|
|
41
|
+
expect(treatmentFor("company-md", byMime("audio/mpeg")!)).toBeNull();
|
|
42
|
+
expect(maxBytesFor("company-md")).toBe(50_000_000);
|
|
30
43
|
});
|
|
31
44
|
|
|
32
|
-
it("
|
|
33
|
-
expect(
|
|
34
|
-
|
|
35
|
-
expect(surfaceAdmits("company-md", byMime("image/png")!)).toBe(false);
|
|
36
|
-
expect(surfaceAdmits("company-md", byMime("audio/mpeg")!)).toBe(false);
|
|
37
|
-
expect(acceptFor("company-md")).toBe(
|
|
38
|
-
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf",
|
|
39
|
-
);
|
|
40
|
-
expect(supportedLabelFor("company-md")).toBe(
|
|
41
|
-
"Supported: Text, Markdown, HTML, CSV, RTF, Word, PowerPoint, Excel, OpenDocument, EPUB, PDF",
|
|
45
|
+
it("org-chart hands documents, images and diagrams to vision", () => {
|
|
46
|
+
expect(treatmentFor("org-chart", byMime("application/pdf")!)).toBe(
|
|
47
|
+
"vision",
|
|
42
48
|
);
|
|
49
|
+
expect(treatmentFor("org-chart", byMime("image/heic")!)).toBe("vision");
|
|
50
|
+
expect(treatmentFor("org-chart", byMime("image/svg+xml")!)).toBe("vision");
|
|
51
|
+
expect(treatmentFor("org-chart", byMime("video/mp4")!)).toBeNull();
|
|
52
|
+
expect(maxBytesFor("org-chart")).toBe(10 * 1024 * 1024);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("chat admits text, documents and images but never media", () => {
|
|
56
|
+
expect(treatmentFor("chat", byMime("text/markdown")!)).toBe("text");
|
|
57
|
+
expect(treatmentFor("chat", byMime("application/pdf")!)).toBe("vision");
|
|
58
|
+
expect(treatmentFor("chat", byMime("image/png")!)).toBe("vision");
|
|
59
|
+
expect(treatmentFor("chat", byMime("audio/mpeg")!)).toBeNull();
|
|
60
|
+
expect(maxBytesFor("chat")).toBe(5 * 1024 * 1024);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("chat caps files per turn and only chat declares a per-turn count", () => {
|
|
64
|
+
expect(INGESTION_SURFACES.chat.maxFilesPerTurn).toBe(8);
|
|
65
|
+
for (const key of ["company-md", "org-chart", "profile-image"] as const) {
|
|
66
|
+
expect(INGESTION_SURFACES[key].maxFilesPerTurn).toBeUndefined();
|
|
67
|
+
}
|
|
43
68
|
});
|
|
44
69
|
|
|
45
|
-
it("
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
48
|
-
expect(
|
|
49
|
-
|
|
50
|
-
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf,.png,.jpg,.jpeg,.webp,.gif,.heic,.svg,.drawio",
|
|
70
|
+
it("profile-image stores images only", () => {
|
|
71
|
+
expect(familiesOf("profile-image")).toEqual(["image"]);
|
|
72
|
+
expect(treatmentFor("profile-image", byMime("image/heif")!)).toBe("store");
|
|
73
|
+
expect(acceptFor("profile-image")).toBe(
|
|
74
|
+
".png,image/png,.jpg,.jpeg,image/jpeg,.webp,image/webp,.gif,image/gif,.heic,image/heic,.heif,image/heif",
|
|
51
75
|
);
|
|
76
|
+
expect(maxBytesFor("profile-image")).toBe(5 * 1024 * 1024);
|
|
52
77
|
});
|
|
53
78
|
|
|
54
|
-
it("
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
79
|
+
it("acceptFor lists every admitted extension and MIME, in registry order", () => {
|
|
80
|
+
for (const key of Object.keys(
|
|
81
|
+
INGESTION_SURFACES,
|
|
82
|
+
) as IngestionSurfaceKey[]) {
|
|
83
|
+
const tokens = acceptFor(key).split(",");
|
|
84
|
+
for (const f of formatsFor(key)) {
|
|
85
|
+
expect(tokens).toContain(f.mime);
|
|
86
|
+
for (const ext of f.extensions) expect(tokens).toContain(ext);
|
|
87
|
+
}
|
|
60
88
|
expect(maxBytesFor(key)).toBeLessThanOrEqual(
|
|
61
|
-
|
|
89
|
+
INGESTION_SURFACES[key].maxBytes,
|
|
62
90
|
);
|
|
63
91
|
}
|
|
64
92
|
});
|
package/src/ingestion/index.ts
CHANGED
|
@@ -13,22 +13,40 @@ export type {
|
|
|
13
13
|
IngestionFormat,
|
|
14
14
|
IngestionSurface,
|
|
15
15
|
IngestionSurfaceKey,
|
|
16
|
+
FormatClass,
|
|
17
|
+
InventoryClass,
|
|
18
|
+
Treatment,
|
|
19
|
+
IngestionProvider,
|
|
20
|
+
IngestionProviderKey,
|
|
21
|
+
ProviderNativeFormat,
|
|
16
22
|
} from "./types";
|
|
17
23
|
|
|
24
|
+
export { FORMAT_CLASSES, TREATMENTS } from "./types";
|
|
25
|
+
|
|
18
26
|
export {
|
|
19
27
|
INGESTION_FORMATS,
|
|
20
28
|
byMime,
|
|
21
29
|
acceptExtensions,
|
|
22
30
|
humanLabels,
|
|
23
|
-
INGEST_ACCEPT,
|
|
24
|
-
SUPPORTED_LABEL,
|
|
25
31
|
} from "./registry";
|
|
26
32
|
|
|
27
33
|
export {
|
|
28
34
|
INGESTION_SURFACES,
|
|
35
|
+
familiesOf,
|
|
36
|
+
treatmentFor,
|
|
29
37
|
formatsFor,
|
|
30
38
|
surfaceAdmits,
|
|
31
39
|
acceptFor,
|
|
32
40
|
supportedLabelFor,
|
|
33
41
|
maxBytesFor,
|
|
34
42
|
} from "./surfaces";
|
|
43
|
+
|
|
44
|
+
export type { ProviderFormat } from "./providers";
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
INGESTION_PROVIDERS,
|
|
48
|
+
providerFormatsFor,
|
|
49
|
+
classOfMime,
|
|
50
|
+
inventoryClassesFor,
|
|
51
|
+
labelOfMime,
|
|
52
|
+
} from "./providers";
|