@company-semantics/contracts 97.0.0 → 99.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/package.json +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +62 -8
- package/src/chat/README.md +31 -2
- package/src/chat/__tests__/alternatives.test.ts +121 -0
- package/src/chat/__tests__/chat-exports.test.ts +73 -1
- package/src/chat/__tests__/feedback-reasons.test.ts +99 -0
- package/src/chat/__tests__/message-actions.test.ts +41 -9
- package/src/chat/__tests__/message-mentions.test.ts +1 -0
- package/src/chat/__tests__/multi-party.test.ts +1 -0
- package/src/chat/__tests__/runtime-profile.test.ts +23 -5
- package/src/chat/__tests__/turn-wire.test.ts +43 -4
- package/src/chat/feedback.ts +80 -0
- package/src/chat/index.ts +26 -0
- package/src/chat/message-actions.ts +36 -30
- package/src/chat/schemas.ts +51 -1
- package/src/chat/types.ts +15 -0
- package/src/generated/openapi-routes.ts +1 -0
- 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
|
@@ -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";
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingestion Providers
|
|
3
|
+
*
|
|
4
|
+
* Provider vocabulary: the native types an external source hands us (exported
|
|
5
|
+
* to a registry format before ingestion) and its structural container MIMEs.
|
|
6
|
+
* Discovery's net, the Drive picker filter, inventory classes and display
|
|
7
|
+
* labels all derive from this plus the surface declarations — no backend or
|
|
8
|
+
* app module keeps its own Workspace MIME table.
|
|
9
|
+
*/
|
|
10
|
+
import { INGESTION_FORMATS, byMime } from "./registry";
|
|
11
|
+
import { surfaceAdmits } from "./surfaces";
|
|
12
|
+
import {
|
|
13
|
+
FORMAT_CLASSES,
|
|
14
|
+
type FormatClass,
|
|
15
|
+
type IngestionFormat,
|
|
16
|
+
type IngestionProvider,
|
|
17
|
+
type IngestionProviderKey,
|
|
18
|
+
type IngestionSurfaceKey,
|
|
19
|
+
type InventoryClass,
|
|
20
|
+
type ProviderNativeFormat,
|
|
21
|
+
} from "./types";
|
|
22
|
+
|
|
23
|
+
/** Every provider's native types and container MIMEs, keyed by provider. */
|
|
24
|
+
export const INGESTION_PROVIDERS: Readonly<
|
|
25
|
+
Record<IngestionProviderKey, IngestionProvider>
|
|
26
|
+
> = {
|
|
27
|
+
"google-drive": {
|
|
28
|
+
key: "google-drive",
|
|
29
|
+
nativeFormats: [
|
|
30
|
+
{
|
|
31
|
+
mime: "application/vnd.google-apps.document",
|
|
32
|
+
label: "Google Doc",
|
|
33
|
+
exportsTo:
|
|
34
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
mime: "application/vnd.google-apps.presentation",
|
|
38
|
+
label: "Google Slides",
|
|
39
|
+
exportsTo:
|
|
40
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
mime: "application/vnd.google-apps.spreadsheet",
|
|
44
|
+
label: "Google Sheet",
|
|
45
|
+
exportsTo:
|
|
46
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
containerMimes: {
|
|
50
|
+
folder: "application/vnd.google-apps.folder",
|
|
51
|
+
shortcut: "application/vnd.google-apps.shortcut",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** One MIME a provider can hand the pipeline, with the registry format it lands as. */
|
|
57
|
+
export interface ProviderFormat {
|
|
58
|
+
/** The MIME as the provider lists it (native or a registry MIME). */
|
|
59
|
+
mime: string;
|
|
60
|
+
/** True for a provider-native type that is exported before ingestion. */
|
|
61
|
+
native: boolean;
|
|
62
|
+
/** The registry format the bytes arrive as (the export target for natives). */
|
|
63
|
+
format: IngestionFormat;
|
|
64
|
+
/** Display label: the native label, else the registry label. */
|
|
65
|
+
label: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function exportTarget(native: ProviderNativeFormat): IngestionFormat {
|
|
69
|
+
const target = byMime(native.exportsTo);
|
|
70
|
+
if (!target) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`ingestion provider: ${native.mime} exports to ${native.exportsTo}, which is not in INGESTION_FORMATS`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return target;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Every MIME the provider can hand a surface (natives first, then registry order). */
|
|
79
|
+
export function providerFormatsFor(
|
|
80
|
+
provider: IngestionProviderKey,
|
|
81
|
+
surface: IngestionSurfaceKey,
|
|
82
|
+
): ProviderFormat[] {
|
|
83
|
+
const natives: ProviderFormat[] = INGESTION_PROVIDERS[
|
|
84
|
+
provider
|
|
85
|
+
].nativeFormats.map((native) => ({
|
|
86
|
+
mime: native.mime,
|
|
87
|
+
native: true,
|
|
88
|
+
format: exportTarget(native),
|
|
89
|
+
label: native.label,
|
|
90
|
+
}));
|
|
91
|
+
const downloads: ProviderFormat[] = INGESTION_FORMATS.map((format) => ({
|
|
92
|
+
mime: format.mime,
|
|
93
|
+
native: false,
|
|
94
|
+
format,
|
|
95
|
+
label: format.label,
|
|
96
|
+
}));
|
|
97
|
+
return [...natives, ...downloads].filter((pf) =>
|
|
98
|
+
surfaceAdmits(surface, pf.format),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The class of a provider-listed MIME; null for containers and unknown types. */
|
|
103
|
+
export function classOfMime(
|
|
104
|
+
provider: IngestionProviderKey,
|
|
105
|
+
mime: string,
|
|
106
|
+
): FormatClass | null {
|
|
107
|
+
const native = INGESTION_PROVIDERS[provider].nativeFormats.find(
|
|
108
|
+
(n) => n.mime === mime,
|
|
109
|
+
);
|
|
110
|
+
if (native) return exportTarget(native).class;
|
|
111
|
+
return byMime(mime)?.class ?? null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The inventory classes discovery can record for a provider feeding a surface,
|
|
116
|
+
* in canonical class order, plus "shortcut". The DB CHECK on
|
|
117
|
+
* source_inventory_items.mime_class is pinned to this.
|
|
118
|
+
*/
|
|
119
|
+
export function inventoryClassesFor(
|
|
120
|
+
provider: IngestionProviderKey,
|
|
121
|
+
surface: IngestionSurfaceKey,
|
|
122
|
+
): InventoryClass[] {
|
|
123
|
+
const present = new Set(
|
|
124
|
+
providerFormatsFor(provider, surface).map((pf) => pf.format.class),
|
|
125
|
+
);
|
|
126
|
+
return [...FORMAT_CLASSES.filter((c) => present.has(c)), "shortcut"];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Display label for a MIME (provider-native first when a provider is given); null if unknown. */
|
|
130
|
+
export function labelOfMime(
|
|
131
|
+
mime: string,
|
|
132
|
+
provider?: IngestionProviderKey,
|
|
133
|
+
): string | null {
|
|
134
|
+
if (provider) {
|
|
135
|
+
const native = INGESTION_PROVIDERS[provider].nativeFormats.find(
|
|
136
|
+
(n) => n.mime === mime,
|
|
137
|
+
);
|
|
138
|
+
if (native) return native.label;
|
|
139
|
+
}
|
|
140
|
+
return byMime(mime)?.label ?? null;
|
|
141
|
+
}
|
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
* Ingestion Format Registry
|
|
3
3
|
*
|
|
4
4
|
* THE single source of truth for the public document-ingestion accept-set. The
|
|
5
|
-
* backend normalizer joins these entries with its private dispatch table;
|
|
6
|
-
*
|
|
7
|
-
* the helpers
|
|
5
|
+
* backend normalizer joins these entries with its private dispatch table; each
|
|
6
|
+
* upload surface derives its file-input `accept` attribute and supported-formats
|
|
7
|
+
* copy from the surface helpers. Neither side re-spells the extension, label or
|
|
8
|
+
* class list.
|
|
8
9
|
*
|
|
9
10
|
* Invariants:
|
|
10
11
|
* - Entries are ordered text → documents → images/diagrams → audio/video; the
|
|
11
12
|
* backend's MIME-order test and the derived UI strings depend on this order.
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
13
|
+
* - Every entry declares its `class` (one of `FORMAT_CLASSES`); no consumer
|
|
14
|
+
* keeps its own extension → class table.
|
|
15
|
+
* - `.drawio` is not listed: no normalizer can rasterize a raw `<mxfile>`, so
|
|
16
|
+
* admitting it would be a lie. It returns when a rasterizer exists.
|
|
14
17
|
*
|
|
15
18
|
* @see ADR-CONT-075 for design rationale
|
|
16
19
|
*/
|
|
@@ -22,6 +25,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
22
25
|
{
|
|
23
26
|
mime: "text/plain",
|
|
24
27
|
family: "text",
|
|
28
|
+
class: "text",
|
|
25
29
|
extensions: [".txt"],
|
|
26
30
|
label: "Text",
|
|
27
31
|
maxBytes: 10_000_000,
|
|
@@ -29,6 +33,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
29
33
|
{
|
|
30
34
|
mime: "text/markdown",
|
|
31
35
|
family: "text",
|
|
36
|
+
class: "text",
|
|
32
37
|
extensions: [".md"],
|
|
33
38
|
label: "Markdown",
|
|
34
39
|
maxBytes: 10_000_000,
|
|
@@ -36,6 +41,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
36
41
|
{
|
|
37
42
|
mime: "text/html",
|
|
38
43
|
family: "text",
|
|
44
|
+
class: "text",
|
|
39
45
|
extensions: [".html", ".htm"],
|
|
40
46
|
label: "HTML",
|
|
41
47
|
maxBytes: 10_000_000,
|
|
@@ -43,6 +49,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
43
49
|
{
|
|
44
50
|
mime: "text/csv",
|
|
45
51
|
family: "text",
|
|
52
|
+
class: "sheets",
|
|
46
53
|
extensions: [".csv"],
|
|
47
54
|
label: "CSV",
|
|
48
55
|
maxBytes: 25_000_000,
|
|
@@ -50,6 +57,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
50
57
|
{
|
|
51
58
|
mime: "application/rtf",
|
|
52
59
|
family: "document",
|
|
60
|
+
class: "docs",
|
|
53
61
|
extensions: [".rtf"],
|
|
54
62
|
label: "RTF",
|
|
55
63
|
maxBytes: 25_000_000,
|
|
@@ -57,6 +65,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
57
65
|
{
|
|
58
66
|
mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
59
67
|
family: "document",
|
|
68
|
+
class: "docs",
|
|
60
69
|
extensions: [".docx"],
|
|
61
70
|
label: "Word",
|
|
62
71
|
maxBytes: 25_000_000,
|
|
@@ -64,6 +73,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
64
73
|
{
|
|
65
74
|
mime: "application/msword",
|
|
66
75
|
family: "document",
|
|
76
|
+
class: "docs",
|
|
67
77
|
extensions: [".doc"],
|
|
68
78
|
label: "Word",
|
|
69
79
|
maxBytes: 25_000_000,
|
|
@@ -71,6 +81,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
71
81
|
{
|
|
72
82
|
mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
73
83
|
family: "document",
|
|
84
|
+
class: "slides",
|
|
74
85
|
extensions: [".pptx"],
|
|
75
86
|
label: "PowerPoint",
|
|
76
87
|
maxBytes: 50_000_000,
|
|
@@ -78,6 +89,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
78
89
|
{
|
|
79
90
|
mime: "application/vnd.ms-powerpoint",
|
|
80
91
|
family: "document",
|
|
92
|
+
class: "slides",
|
|
81
93
|
extensions: [".ppt"],
|
|
82
94
|
label: "PowerPoint",
|
|
83
95
|
maxBytes: 50_000_000,
|
|
@@ -85,6 +97,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
85
97
|
{
|
|
86
98
|
mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
87
99
|
family: "document",
|
|
100
|
+
class: "sheets",
|
|
88
101
|
extensions: [".xlsx"],
|
|
89
102
|
label: "Excel",
|
|
90
103
|
maxBytes: 50_000_000,
|
|
@@ -92,6 +105,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
92
105
|
{
|
|
93
106
|
mime: "application/vnd.ms-excel",
|
|
94
107
|
family: "document",
|
|
108
|
+
class: "sheets",
|
|
95
109
|
extensions: [".xls"],
|
|
96
110
|
label: "Excel",
|
|
97
111
|
maxBytes: 50_000_000,
|
|
@@ -99,6 +113,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
99
113
|
{
|
|
100
114
|
mime: "application/vnd.oasis.opendocument.text",
|
|
101
115
|
family: "document",
|
|
116
|
+
class: "docs",
|
|
102
117
|
extensions: [".odt"],
|
|
103
118
|
label: "OpenDocument",
|
|
104
119
|
maxBytes: 25_000_000,
|
|
@@ -106,6 +121,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
106
121
|
{
|
|
107
122
|
mime: "application/vnd.oasis.opendocument.spreadsheet",
|
|
108
123
|
family: "document",
|
|
124
|
+
class: "sheets",
|
|
109
125
|
extensions: [".ods"],
|
|
110
126
|
label: "OpenDocument",
|
|
111
127
|
maxBytes: 50_000_000,
|
|
@@ -113,6 +129,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
113
129
|
{
|
|
114
130
|
mime: "application/vnd.oasis.opendocument.presentation",
|
|
115
131
|
family: "document",
|
|
132
|
+
class: "slides",
|
|
116
133
|
extensions: [".odp"],
|
|
117
134
|
label: "OpenDocument",
|
|
118
135
|
maxBytes: 50_000_000,
|
|
@@ -120,6 +137,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
120
137
|
{
|
|
121
138
|
mime: "application/epub+zip",
|
|
122
139
|
family: "document",
|
|
140
|
+
class: "docs",
|
|
123
141
|
extensions: [".epub"],
|
|
124
142
|
label: "EPUB",
|
|
125
143
|
maxBytes: 25_000_000,
|
|
@@ -127,6 +145,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
127
145
|
{
|
|
128
146
|
mime: "application/pdf",
|
|
129
147
|
family: "document",
|
|
148
|
+
class: "pdf",
|
|
130
149
|
extensions: [".pdf"],
|
|
131
150
|
label: "PDF",
|
|
132
151
|
maxBytes: 50_000_000,
|
|
@@ -134,6 +153,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
134
153
|
{
|
|
135
154
|
mime: "image/png",
|
|
136
155
|
family: "image",
|
|
156
|
+
class: "image",
|
|
137
157
|
extensions: [".png"],
|
|
138
158
|
label: "Image",
|
|
139
159
|
maxBytes: 25_000_000,
|
|
@@ -141,6 +161,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
141
161
|
{
|
|
142
162
|
mime: "image/jpeg",
|
|
143
163
|
family: "image",
|
|
164
|
+
class: "image",
|
|
144
165
|
extensions: [".jpg", ".jpeg"],
|
|
145
166
|
label: "Image",
|
|
146
167
|
maxBytes: 25_000_000,
|
|
@@ -148,6 +169,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
148
169
|
{
|
|
149
170
|
mime: "image/webp",
|
|
150
171
|
family: "image",
|
|
172
|
+
class: "image",
|
|
151
173
|
extensions: [".webp"],
|
|
152
174
|
label: "Image",
|
|
153
175
|
maxBytes: 25_000_000,
|
|
@@ -155,6 +177,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
155
177
|
{
|
|
156
178
|
mime: "image/gif",
|
|
157
179
|
family: "image",
|
|
180
|
+
class: "image",
|
|
158
181
|
extensions: [".gif"],
|
|
159
182
|
label: "Image",
|
|
160
183
|
maxBytes: 25_000_000,
|
|
@@ -162,27 +185,31 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
162
185
|
{
|
|
163
186
|
mime: "image/heic",
|
|
164
187
|
family: "image",
|
|
188
|
+
class: "image",
|
|
165
189
|
extensions: [".heic"],
|
|
166
190
|
label: "Image",
|
|
167
191
|
maxBytes: 25_000_000,
|
|
168
192
|
},
|
|
169
193
|
{
|
|
170
|
-
mime: "image/
|
|
171
|
-
family: "
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
194
|
+
mime: "image/heif",
|
|
195
|
+
family: "image",
|
|
196
|
+
class: "image",
|
|
197
|
+
extensions: [".heif"],
|
|
198
|
+
label: "Image",
|
|
199
|
+
maxBytes: 25_000_000,
|
|
175
200
|
},
|
|
176
201
|
{
|
|
177
|
-
mime: "
|
|
202
|
+
mime: "image/svg+xml",
|
|
178
203
|
family: "diagram",
|
|
179
|
-
|
|
204
|
+
class: "diagram",
|
|
205
|
+
extensions: [".svg"],
|
|
180
206
|
label: "Diagram",
|
|
181
207
|
maxBytes: 10_000_000,
|
|
182
208
|
},
|
|
183
209
|
{
|
|
184
210
|
mime: "audio/mpeg",
|
|
185
211
|
family: "audio",
|
|
212
|
+
class: "audio",
|
|
186
213
|
extensions: [".mp3"],
|
|
187
214
|
label: "Audio",
|
|
188
215
|
maxBytes: 500_000_000,
|
|
@@ -190,6 +217,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
190
217
|
{
|
|
191
218
|
mime: "audio/x-m4a",
|
|
192
219
|
family: "audio",
|
|
220
|
+
class: "audio",
|
|
193
221
|
extensions: [".m4a"],
|
|
194
222
|
label: "Audio",
|
|
195
223
|
maxBytes: 500_000_000,
|
|
@@ -197,6 +225,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
197
225
|
{
|
|
198
226
|
mime: "audio/wav",
|
|
199
227
|
family: "audio",
|
|
228
|
+
class: "audio",
|
|
200
229
|
extensions: [".wav"],
|
|
201
230
|
label: "Audio",
|
|
202
231
|
maxBytes: 500_000_000,
|
|
@@ -204,6 +233,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
204
233
|
{
|
|
205
234
|
mime: "audio/aac",
|
|
206
235
|
family: "audio",
|
|
236
|
+
class: "audio",
|
|
207
237
|
extensions: [".aac"],
|
|
208
238
|
label: "Audio",
|
|
209
239
|
maxBytes: 500_000_000,
|
|
@@ -211,6 +241,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
211
241
|
{
|
|
212
242
|
mime: "video/mp4",
|
|
213
243
|
family: "video",
|
|
244
|
+
class: "video",
|
|
214
245
|
extensions: [".mp4", ".m4v"],
|
|
215
246
|
label: "Video",
|
|
216
247
|
maxBytes: 500_000_000,
|
|
@@ -218,6 +249,7 @@ export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
|
218
249
|
{
|
|
219
250
|
mime: "video/quicktime",
|
|
220
251
|
family: "video",
|
|
252
|
+
class: "video",
|
|
221
253
|
extensions: [".mov"],
|
|
222
254
|
label: "Video",
|
|
223
255
|
maxBytes: 500_000_000,
|
|
@@ -238,16 +270,3 @@ export function acceptExtensions(): string[] {
|
|
|
238
270
|
export function humanLabels(): string[] {
|
|
239
271
|
return [...new Set(INGESTION_FORMATS.map((entry) => entry.label))];
|
|
240
272
|
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Comma-joined extension string for a file input's `accept` attribute.
|
|
244
|
-
* Derived from {@link acceptExtensions} — never hand-maintained.
|
|
245
|
-
*/
|
|
246
|
-
export const INGEST_ACCEPT: string = acceptExtensions().join(",");
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Human-readable supported-formats summary, derived from {@link humanLabels}.
|
|
250
|
-
* Names every accepted group (in registry order) so the copy can't undersell
|
|
251
|
-
* what actually works.
|
|
252
|
-
*/
|
|
253
|
-
export const SUPPORTED_LABEL: string = `Supported: ${humanLabels().join(", ")}`;
|