@vellumai/assistant 0.10.6-staging.1 → 0.10.6
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/openapi.yaml +43 -6
- package/package.json +1 -1
- package/src/__tests__/attachment-stored-path-annotation.test.ts +151 -0
- package/src/__tests__/attachments-store.test.ts +37 -1
- package/src/__tests__/attachments.test.ts +53 -0
- package/src/__tests__/channel-reply-delivery.test.ts +214 -5
- package/src/__tests__/config-loader-backfill.test.ts +16 -36
- package/src/__tests__/image-source-path-reinject.test.ts +155 -14
- package/src/__tests__/managed-profile-guard.test.ts +2 -0
- package/src/__tests__/mtime-cache.test.ts +265 -80
- package/src/__tests__/plugin-config-data-migration.test.ts +2 -4
- package/src/__tests__/plugin-effective-enabled-set.test.ts +9 -6
- package/src/__tests__/workspace-migration-125-repoint-managed-connections-to-vellum.test.ts +140 -0
- package/src/agent/attachments.ts +55 -13
- package/src/cli/commands/platform/__tests__/credits.test.ts +111 -0
- package/src/cli/commands/platform/index.ts +86 -4
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +9 -9
- package/src/cli/lib/diff-plugin.ts +7 -3
- package/src/cli/lib/inspect-plugin.ts +28 -10
- package/src/cli/lib/install-from-github.ts +69 -24
- package/src/cli/lib/ipc-params.ts +3 -3
- package/src/cli/lib/list-installed-plugins.ts +11 -5
- package/src/cli/lib/merge-plugin-tree.ts +15 -9
- package/src/cli/lib/plugin-fingerprint.ts +0 -6
- package/src/cli/lib/publish-plugin.ts +1 -1
- package/src/cli/lib/unknown-command.ts +3 -1
- package/src/cli/lib/upgrade-plugin.ts +2 -5
- package/src/config/__tests__/sync-gated-profiles.test.ts +2 -2
- package/src/config/seed-inference-profiles.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +37 -15
- package/src/daemon/conversation-messaging.ts +56 -28
- package/src/daemon/conversation.ts +2 -2
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -0
- package/src/hooks/__tests__/hook-live-reload.test.ts +110 -96
- package/src/hooks/hook-loader.ts +57 -144
- package/src/hooks/registry.ts +13 -9
- package/src/persistence/attachments-store.ts +2 -1
- package/src/persistence/conversation-crud.ts +24 -0
- package/src/plugin-api/types.ts +4 -1
- package/src/plugins/mtime-cache.ts +347 -33
- package/src/plugins/pipeline.ts +3 -2
- package/src/providers/__tests__/inference.test.ts +42 -31
- package/src/providers/inference/__tests__/connections-label.test.ts +9 -28
- package/src/providers/inference/auth.ts +1 -2
- package/src/providers/inference/backfill.ts +7 -5
- package/src/providers/inference/connections.ts +54 -30
- package/src/runtime/channel-reply-delivery.ts +83 -24
- package/src/runtime/channel-retry-sweep.ts +4 -0
- package/src/runtime/{slack-no-response.ts → no-response.ts} +26 -6
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +43 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +61 -24
- package/src/runtime/routes/conversation-query-routes.ts +18 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +1 -1
- package/src/runtime/routes/inference-provider-connection-routes.ts +4 -3
- package/src/runtime/routes/platform-routes.ts +82 -1
- package/src/runtime/slack-reply-session.ts +1 -1
- package/src/workspace/migrations/125-repoint-managed-connections-to-vellum.ts +99 -0
- package/src/workspace/migrations/registry.ts +2 -0
|
@@ -596,9 +596,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
596
596
|
expect(config.llm.profiles.balanced?.model).toBe(
|
|
597
597
|
"accounts/fireworks/models/glm-5p2",
|
|
598
598
|
);
|
|
599
|
-
expect(config.llm.profiles.balanced?.provider_connection).toBe(
|
|
600
|
-
"fireworks-managed",
|
|
601
|
-
);
|
|
599
|
+
expect(config.llm.profiles.balanced?.provider_connection).toBe("vellum");
|
|
602
600
|
|
|
603
601
|
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
604
602
|
expect(raw.llm.default).toEqual({
|
|
@@ -639,9 +637,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
639
637
|
expect(config.llm.profiles.balanced?.model).toBe(
|
|
640
638
|
"accounts/fireworks/models/glm-5p2",
|
|
641
639
|
);
|
|
642
|
-
expect(config.llm.profiles.balanced?.provider_connection).toBe(
|
|
643
|
-
"fireworks-managed",
|
|
644
|
-
);
|
|
640
|
+
expect(config.llm.profiles.balanced?.provider_connection).toBe("vellum");
|
|
645
641
|
// No user profiles created on platform.
|
|
646
642
|
expect(config.llm.profiles["custom-balanced"]).toBeUndefined();
|
|
647
643
|
});
|
|
@@ -684,9 +680,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
684
680
|
);
|
|
685
681
|
// Managed balanced profile is seeded for fireworks-managed (GLM 5.2).
|
|
686
682
|
expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
|
|
687
|
-
expect(raw.llm.profiles.balanced.provider_connection).toBe(
|
|
688
|
-
"fireworks-managed",
|
|
689
|
-
);
|
|
683
|
+
expect(raw.llm.profiles.balanced.provider_connection).toBe("vellum");
|
|
690
684
|
});
|
|
691
685
|
|
|
692
686
|
test("on-platform re-hatch resets active profile to balanced", () => {
|
|
@@ -720,9 +714,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
720
714
|
// On-platform: no user profiles created, active resets to managed balanced.
|
|
721
715
|
expect(raw.llm.activeProfile).toBe("balanced");
|
|
722
716
|
expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
|
|
723
|
-
expect(raw.llm.profiles.balanced.provider_connection).toBe(
|
|
724
|
-
"fireworks-managed",
|
|
725
|
-
);
|
|
717
|
+
expect(raw.llm.profiles.balanced.provider_connection).toBe("vellum");
|
|
726
718
|
// The old custom-balanced is preserved on disk but no longer active.
|
|
727
719
|
expect(raw.llm.profiles["custom-balanced"].provider).toBe("openai");
|
|
728
720
|
});
|
|
@@ -848,9 +840,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
848
840
|
// Managed profiles are also seeded. Balanced now serves GLM 5.2 on
|
|
849
841
|
// Fireworks (the model Quality used to carry).
|
|
850
842
|
expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
|
|
851
|
-
expect(raw.llm.profiles.balanced.provider_connection).toBe(
|
|
852
|
-
"fireworks-managed",
|
|
853
|
-
);
|
|
843
|
+
expect(raw.llm.profiles.balanced.provider_connection).toBe("vellum");
|
|
854
844
|
expect(raw.llm.profiles.balanced.model).toBe(
|
|
855
845
|
"accounts/fireworks/models/glm-5p2",
|
|
856
846
|
);
|
|
@@ -890,9 +880,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
890
880
|
expect(raw.llm.profiles.balanced.model).toBe(
|
|
891
881
|
"accounts/fireworks/models/glm-5p2",
|
|
892
882
|
);
|
|
893
|
-
expect(raw.llm.profiles.balanced.provider_connection).toBe(
|
|
894
|
-
"fireworks-managed",
|
|
895
|
-
);
|
|
883
|
+
expect(raw.llm.profiles.balanced.provider_connection).toBe("vellum");
|
|
896
884
|
expect(raw.llm.activeProfile).toBe("balanced");
|
|
897
885
|
});
|
|
898
886
|
|
|
@@ -961,9 +949,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
961
949
|
// The template carries no topP, and the previous entry had none, so the
|
|
962
950
|
// reconciled profile has no topP override.
|
|
963
951
|
expect("topP" in raw.llm.profiles.balanced).toBe(false);
|
|
964
|
-
expect(raw.llm.profiles.balanced.provider_connection).toBe(
|
|
965
|
-
"fireworks-managed",
|
|
966
|
-
);
|
|
952
|
+
expect(raw.llm.profiles.balanced.provider_connection).toBe("vellum");
|
|
967
953
|
expect(raw.llm.activeProfile).toBe("balanced");
|
|
968
954
|
});
|
|
969
955
|
|
|
@@ -1234,7 +1220,7 @@ describe("loadConfig startup behavior", () => {
|
|
|
1234
1220
|
expect(afterRestart.llm.activeProfile).toBe("balanced");
|
|
1235
1221
|
expect(afterRestart.llm.profiles.balanced.provider).toBe("fireworks");
|
|
1236
1222
|
expect(afterRestart.llm.profiles.balanced.provider_connection).toBe(
|
|
1237
|
-
"
|
|
1223
|
+
"vellum",
|
|
1238
1224
|
);
|
|
1239
1225
|
expect(afterRestart.llm.profiles.balanced.model).toBe(
|
|
1240
1226
|
"accounts/fireworks/models/glm-5p2",
|
|
@@ -1574,19 +1560,13 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
|
|
|
1574
1560
|
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1575
1561
|
|
|
1576
1562
|
expect(raw.llm.activeProfile).toBe("balanced");
|
|
1577
|
-
//
|
|
1578
|
-
// `
|
|
1579
|
-
//
|
|
1580
|
-
// profile, `
|
|
1581
|
-
expect(raw.llm.advisorProfile).toBe("
|
|
1582
|
-
expect(raw.llm.profiles.balanced.provider_connection).toBe(
|
|
1583
|
-
"fireworks-managed",
|
|
1584
|
-
);
|
|
1563
|
+
// All managed profiles now share the single `vellum` connection, so
|
|
1564
|
+
// selecting `balanced` at hatch activates that connection for every managed
|
|
1565
|
+
// profile — none are disabled. The default advisor falls to the strongest
|
|
1566
|
+
// active managed profile, `quality-optimized`.
|
|
1567
|
+
expect(raw.llm.advisorProfile).toBe("quality-optimized");
|
|
1568
|
+
expect(raw.llm.profiles.balanced.provider_connection).toBe("vellum");
|
|
1585
1569
|
expect("status" in raw.llm.profiles.balanced).toBe(false);
|
|
1586
|
-
// Connections exist (status is no longer a connection-level concept).
|
|
1587
|
-
expect(getConnection(db, "anthropic-managed")).not.toBeNull();
|
|
1588
|
-
expect(getConnection(db, "openai-managed")).not.toBeNull();
|
|
1589
|
-
expect(getConnection(db, "gemini-managed")).not.toBeNull();
|
|
1590
1570
|
});
|
|
1591
1571
|
|
|
1592
1572
|
test("off-platform managed-inference hatch respects explicit non-managed active connection", () => {
|
|
@@ -1883,11 +1863,11 @@ describe("OS Beta managed profile template", () => {
|
|
|
1883
1863
|
const entry = materializeProfile(
|
|
1884
1864
|
OS_BETA_PROFILE_TEMPLATE,
|
|
1885
1865
|
"together",
|
|
1886
|
-
"
|
|
1866
|
+
"vellum",
|
|
1887
1867
|
);
|
|
1888
1868
|
|
|
1889
1869
|
expect(entry.model).toBe("MiniMaxAI/MiniMax-M3");
|
|
1890
|
-
expect(entry.provider_connection).toBe("
|
|
1870
|
+
expect(entry.provider_connection).toBe("vellum");
|
|
1891
1871
|
expect(entry.provider).toBe("together");
|
|
1892
1872
|
expect(entry.label).toBe("OS Beta");
|
|
1893
1873
|
expect(entry.source).toBe("managed");
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { enrichMessageWithSourcePaths } from "../agent/attachments.js";
|
|
4
|
+
import { createUserMessage } from "../agent/message-types.js";
|
|
5
|
+
import { reinjectAttachmentPathAnnotations } from "../daemon/conversation-lifecycle.js";
|
|
6
|
+
import {
|
|
7
|
+
extractAttachmentStoredPaths,
|
|
8
|
+
extractImageSourcePaths,
|
|
9
|
+
} from "../persistence/conversation-crud.js";
|
|
4
10
|
import type { ContentBlock } from "../providers/types.js";
|
|
5
11
|
|
|
6
12
|
// ---------------------------------------------------------------------------
|
|
7
|
-
//
|
|
8
|
-
//
|
|
13
|
+
// reinjectAttachmentPathAnnotations — re-inject attachment path annotations
|
|
14
|
+
// (image source paths + resolved stored paths) when loading conversation
|
|
15
|
+
// history from DB
|
|
9
16
|
// ---------------------------------------------------------------------------
|
|
10
17
|
|
|
11
|
-
describe("
|
|
18
|
+
describe("reinjectAttachmentPathAnnotations", () => {
|
|
12
19
|
const baseContent: ContentBlock[] = [
|
|
13
20
|
{ type: "text", text: "what is this?" },
|
|
14
21
|
{
|
|
@@ -21,7 +28,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
21
28
|
const metadata = JSON.stringify({
|
|
22
29
|
imageSourcePaths: { "photo.jpg": "/Users/me/Desktop/photo.jpg" },
|
|
23
30
|
});
|
|
24
|
-
const result =
|
|
31
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
32
|
+
baseContent,
|
|
33
|
+
"user",
|
|
34
|
+
metadata,
|
|
35
|
+
);
|
|
25
36
|
|
|
26
37
|
expect(result).toHaveLength(3);
|
|
27
38
|
const annotation = result[2] as { type: "text"; text: string };
|
|
@@ -35,7 +46,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
35
46
|
const metadata = JSON.stringify({
|
|
36
47
|
imageSourcePaths: { "photo.jpg": "/Users/me/Desktop/photo.jpg" },
|
|
37
48
|
});
|
|
38
|
-
const result =
|
|
49
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
50
|
+
baseContent,
|
|
51
|
+
"assistant",
|
|
52
|
+
metadata,
|
|
53
|
+
);
|
|
39
54
|
|
|
40
55
|
// Should return the original content unchanged
|
|
41
56
|
expect(result).toBe(baseContent);
|
|
@@ -43,7 +58,7 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
43
58
|
});
|
|
44
59
|
|
|
45
60
|
test("returns content unchanged when metadata is null", () => {
|
|
46
|
-
const result =
|
|
61
|
+
const result = reinjectAttachmentPathAnnotations(baseContent, "user", null);
|
|
47
62
|
expect(result).toBe(baseContent);
|
|
48
63
|
expect(result).toHaveLength(2);
|
|
49
64
|
});
|
|
@@ -52,7 +67,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
52
67
|
const metadata = JSON.stringify({
|
|
53
68
|
userMessageChannel: "desktop",
|
|
54
69
|
});
|
|
55
|
-
const result =
|
|
70
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
71
|
+
baseContent,
|
|
72
|
+
"user",
|
|
73
|
+
metadata,
|
|
74
|
+
);
|
|
56
75
|
expect(result).toBe(baseContent);
|
|
57
76
|
expect(result).toHaveLength(2);
|
|
58
77
|
});
|
|
@@ -61,7 +80,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
61
80
|
const metadata = JSON.stringify({
|
|
62
81
|
imageSourcePaths: {},
|
|
63
82
|
});
|
|
64
|
-
const result =
|
|
83
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
84
|
+
baseContent,
|
|
85
|
+
"user",
|
|
86
|
+
metadata,
|
|
87
|
+
);
|
|
65
88
|
expect(result).toBe(baseContent);
|
|
66
89
|
expect(result).toHaveLength(2);
|
|
67
90
|
});
|
|
@@ -73,7 +96,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
73
96
|
"b.png": "/path/to/b.png",
|
|
74
97
|
},
|
|
75
98
|
});
|
|
76
|
-
const result =
|
|
99
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
100
|
+
baseContent,
|
|
101
|
+
"user",
|
|
102
|
+
metadata,
|
|
103
|
+
);
|
|
77
104
|
|
|
78
105
|
expect(result).toHaveLength(3);
|
|
79
106
|
const annotation = result[2] as { type: "text"; text: string };
|
|
@@ -84,7 +111,7 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
84
111
|
});
|
|
85
112
|
|
|
86
113
|
test("gracefully handles malformed metadata JSON", () => {
|
|
87
|
-
const result =
|
|
114
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
88
115
|
baseContent,
|
|
89
116
|
"user",
|
|
90
117
|
"not-valid-json{{{",
|
|
@@ -102,7 +129,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
102
129
|
"also_bad.jpg": null,
|
|
103
130
|
},
|
|
104
131
|
});
|
|
105
|
-
const result =
|
|
132
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
133
|
+
baseContent,
|
|
134
|
+
"user",
|
|
135
|
+
metadata,
|
|
136
|
+
);
|
|
106
137
|
|
|
107
138
|
expect(result).toHaveLength(3);
|
|
108
139
|
const annotation = result[2] as { type: "text"; text: string };
|
|
@@ -118,7 +149,11 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
118
149
|
"also_bad.jpg": null,
|
|
119
150
|
},
|
|
120
151
|
});
|
|
121
|
-
const result =
|
|
152
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
153
|
+
baseContent,
|
|
154
|
+
"user",
|
|
155
|
+
metadata,
|
|
156
|
+
);
|
|
122
157
|
expect(result).toBe(baseContent);
|
|
123
158
|
expect(result).toHaveLength(2);
|
|
124
159
|
});
|
|
@@ -127,10 +162,116 @@ describe("reinjectImageSourcePaths", () => {
|
|
|
127
162
|
const metadata = JSON.stringify({
|
|
128
163
|
imageSourcePaths: { "photo.jpg": "/path/photo.jpg" },
|
|
129
164
|
});
|
|
130
|
-
const result =
|
|
165
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
166
|
+
baseContent,
|
|
167
|
+
"user",
|
|
168
|
+
metadata,
|
|
169
|
+
);
|
|
131
170
|
|
|
132
171
|
// First two blocks should be identical to the originals
|
|
133
172
|
expect(result[0]).toEqual(baseContent[0]);
|
|
134
173
|
expect(result[1]).toEqual(baseContent[1]);
|
|
135
174
|
});
|
|
175
|
+
|
|
176
|
+
test("adds stored path annotations from attachmentStoredPaths", () => {
|
|
177
|
+
const metadata = JSON.stringify({
|
|
178
|
+
attachmentStoredPaths: {
|
|
179
|
+
"0:report.pdf": "/conv/attachments/report-2.pdf",
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
183
|
+
baseContent,
|
|
184
|
+
"user",
|
|
185
|
+
metadata,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(result).toHaveLength(3);
|
|
189
|
+
const annotation = result[2] as { type: "text"; text: string };
|
|
190
|
+
expect(annotation.text).toBe(
|
|
191
|
+
'[Attachment "report.pdf" is stored at: /conv/attachments/report-2.pdf]',
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("recovers filenames containing colons from stored path keys", () => {
|
|
196
|
+
const metadata = JSON.stringify({
|
|
197
|
+
attachmentStoredPaths: {
|
|
198
|
+
"0:notes: draft.txt": "/conv/attachments/notes: draft.txt",
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
202
|
+
baseContent,
|
|
203
|
+
"user",
|
|
204
|
+
metadata,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const annotation = result[2] as { type: "text"; text: string };
|
|
208
|
+
expect(annotation.text).toBe(
|
|
209
|
+
'[Attachment "notes: draft.txt" is stored at: /conv/attachments/notes: draft.txt]',
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("emits image source lines before stored path lines in one block", () => {
|
|
214
|
+
const metadata = JSON.stringify({
|
|
215
|
+
imageSourcePaths: { "1:photo.jpg": "/Users/me/Desktop/photo.jpg" },
|
|
216
|
+
attachmentStoredPaths: {
|
|
217
|
+
"0:data.csv": "/conv/attachments/data-2.csv",
|
|
218
|
+
"1:photo.jpg": "/conv/attachments/photo.jpg",
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
const result = reinjectAttachmentPathAnnotations(
|
|
222
|
+
baseContent,
|
|
223
|
+
"user",
|
|
224
|
+
metadata,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
expect(result).toHaveLength(3);
|
|
228
|
+
const annotation = result[2] as { type: "text"; text: string };
|
|
229
|
+
expect(annotation.text).toBe(
|
|
230
|
+
"[Attached image source: /Users/me/Desktop/photo.jpg]\n" +
|
|
231
|
+
'[Attachment "data.csv" is stored at: /conv/attachments/data-2.csv]\n' +
|
|
232
|
+
'[Attachment "photo.jpg" is stored at: /conv/attachments/photo.jpg]',
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("rebuilds the exact annotation block enrichMessageWithSourcePaths appends", () => {
|
|
237
|
+
// Prefix-cache parity tripwire: the block appended at persist time (from
|
|
238
|
+
// live attachment inputs) and the block rebuilt on history reload (from
|
|
239
|
+
// persisted metadata) must be byte-identical.
|
|
240
|
+
const attachments = [
|
|
241
|
+
{
|
|
242
|
+
filename: "data.csv",
|
|
243
|
+
mimeType: "text/csv",
|
|
244
|
+
data: "csvdata",
|
|
245
|
+
storedPath: "/conv/attachments/data-2.csv",
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
filename: "photo.jpg",
|
|
249
|
+
mimeType: "image/jpeg",
|
|
250
|
+
data: "img",
|
|
251
|
+
filePath: "/Users/me/Desktop/photo.jpg",
|
|
252
|
+
storedPath: "/conv/attachments/photo.jpg",
|
|
253
|
+
},
|
|
254
|
+
];
|
|
255
|
+
const enriched = enrichMessageWithSourcePaths(
|
|
256
|
+
createUserMessage("compare", attachments),
|
|
257
|
+
attachments,
|
|
258
|
+
);
|
|
259
|
+
const liveBlock = enriched.content.at(-1) as {
|
|
260
|
+
type: "text";
|
|
261
|
+
text: string;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const metadata = JSON.stringify({
|
|
265
|
+
imageSourcePaths: extractImageSourcePaths(attachments),
|
|
266
|
+
attachmentStoredPaths: extractAttachmentStoredPaths(attachments),
|
|
267
|
+
});
|
|
268
|
+
const rebuilt = reinjectAttachmentPathAnnotations(
|
|
269
|
+
[{ type: "text", text: "compare" }],
|
|
270
|
+
"user",
|
|
271
|
+
metadata,
|
|
272
|
+
);
|
|
273
|
+
const rebuiltBlock = rebuilt.at(-1) as { type: "text"; text: string };
|
|
274
|
+
|
|
275
|
+
expect(rebuiltBlock.text).toBe(liveBlock.text);
|
|
276
|
+
});
|
|
136
277
|
});
|
|
@@ -120,6 +120,8 @@ mock.module("../persistence/embeddings/embedding-backend.js", () => ({
|
|
|
120
120
|
mock.module("../providers/inference/connections.js", () => ({
|
|
121
121
|
listConnections: () => [],
|
|
122
122
|
createConnection: () => ({ ok: false, error: { code: "already_exists" } }),
|
|
123
|
+
getConnection: () => null,
|
|
124
|
+
VELLUM_MANAGED_CONNECTION_NAME: "vellum",
|
|
123
125
|
PROVIDERS_REQUIRING_BASE_URL_AND_MODELS: new Set(["openai-compatible"]),
|
|
124
126
|
}));
|
|
125
127
|
|