@convex-dev/agent 0.1.4-alpha.0 → 0.1.5-alpha.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/README.md +24 -35
- package/dist/commonjs/client/createTool.d.ts +3 -5
- package/dist/commonjs/client/createTool.d.ts.map +1 -1
- package/dist/commonjs/client/createTool.js +1 -0
- package/dist/commonjs/client/createTool.js.map +1 -1
- package/dist/commonjs/client/index.d.ts +6 -6
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/streaming.d.ts +5 -5
- package/dist/commonjs/client/streaming.d.ts.map +1 -1
- package/dist/commonjs/client/streaming.js +3 -2
- package/dist/commonjs/client/streaming.js.map +1 -1
- package/dist/commonjs/component/files.d.ts +2 -2
- package/dist/commonjs/component/files.d.ts.map +1 -1
- package/dist/commonjs/component/files.js.map +1 -1
- package/dist/commonjs/component/messages.d.ts +24 -24
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +4 -5
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +45 -45
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/streams.d.ts.map +1 -1
- package/dist/commonjs/component/streams.js +4 -7
- package/dist/commonjs/component/streams.js.map +1 -1
- package/dist/commonjs/component/threads.d.ts +8 -8
- package/dist/commonjs/component/threads.js +3 -3
- package/dist/commonjs/component/threads.js.map +1 -1
- package/dist/commonjs/component/users.js +4 -4
- package/dist/commonjs/component/users.js.map +1 -1
- package/dist/commonjs/component/vector/index.d.ts +3 -3
- package/dist/commonjs/component/vector/index.d.ts.map +1 -1
- package/dist/commonjs/component/vector/index.js +4 -5
- package/dist/commonjs/component/vector/index.js.map +1 -1
- package/dist/commonjs/component/vector/tables.d.ts +5 -7
- package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
- package/dist/commonjs/mapping.d.ts +2 -2
- package/dist/commonjs/mapping.d.ts.map +1 -1
- package/dist/commonjs/mapping.js.map +1 -1
- package/dist/commonjs/react/deltas.d.ts +29 -0
- package/dist/commonjs/react/deltas.d.ts.map +1 -0
- package/dist/commonjs/react/deltas.js +267 -0
- package/dist/commonjs/react/deltas.js.map +1 -0
- package/dist/commonjs/react/index.d.ts +4 -49
- package/dist/commonjs/react/index.d.ts.map +1 -1
- package/dist/commonjs/react/index.js +15 -446
- package/dist/commonjs/react/index.js.map +1 -1
- package/dist/commonjs/react/optimisticallySendMessage.d.ts +8 -0
- package/dist/commonjs/react/optimisticallySendMessage.d.ts.map +1 -0
- package/dist/commonjs/react/optimisticallySendMessage.js +40 -0
- package/dist/commonjs/react/optimisticallySendMessage.js.map +1 -0
- package/dist/commonjs/react/toUIMessages.d.ts +3 -3
- package/dist/commonjs/react/toUIMessages.d.ts.map +1 -1
- package/dist/commonjs/react/toUIMessages.js +4 -2
- package/dist/commonjs/react/toUIMessages.js.map +1 -1
- package/dist/commonjs/react/types.d.ts +26 -0
- package/dist/commonjs/react/types.d.ts.map +1 -0
- package/dist/commonjs/react/types.js +2 -0
- package/dist/commonjs/react/types.js.map +1 -0
- package/dist/commonjs/react/useSmoothText.d.ts +20 -0
- package/dist/commonjs/react/useSmoothText.d.ts.map +1 -0
- package/dist/commonjs/react/useSmoothText.js +50 -0
- package/dist/commonjs/react/useSmoothText.js.map +1 -0
- package/dist/commonjs/shared.d.ts +2 -2
- package/dist/commonjs/shared.d.ts.map +1 -1
- package/dist/commonjs/validators.d.ts +1 -1
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +2 -2
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/commonjs.tsbuildinfo +1 -0
- package/dist/esm/client/createTool.d.ts +3 -5
- package/dist/esm/client/createTool.d.ts.map +1 -1
- package/dist/esm/client/createTool.js +1 -0
- package/dist/esm/client/createTool.js.map +1 -1
- package/dist/esm/client/index.d.ts +6 -6
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/streaming.d.ts +5 -5
- package/dist/esm/client/streaming.d.ts.map +1 -1
- package/dist/esm/client/streaming.js +3 -2
- package/dist/esm/client/streaming.js.map +1 -1
- package/dist/esm/component/files.d.ts +2 -2
- package/dist/esm/component/files.d.ts.map +1 -1
- package/dist/esm/component/files.js.map +1 -1
- package/dist/esm/component/messages.d.ts +24 -24
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +4 -5
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +45 -45
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/streams.d.ts.map +1 -1
- package/dist/esm/component/streams.js +4 -7
- package/dist/esm/component/streams.js.map +1 -1
- package/dist/esm/component/threads.d.ts +8 -8
- package/dist/esm/component/threads.js +3 -3
- package/dist/esm/component/threads.js.map +1 -1
- package/dist/esm/component/users.js +4 -4
- package/dist/esm/component/users.js.map +1 -1
- package/dist/esm/component/vector/index.d.ts +3 -3
- package/dist/esm/component/vector/index.d.ts.map +1 -1
- package/dist/esm/component/vector/index.js +4 -5
- package/dist/esm/component/vector/index.js.map +1 -1
- package/dist/esm/component/vector/tables.d.ts +5 -7
- package/dist/esm/component/vector/tables.d.ts.map +1 -1
- package/dist/esm/mapping.d.ts +2 -2
- package/dist/esm/mapping.d.ts.map +1 -1
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/react/deltas.d.ts +29 -0
- package/dist/esm/react/deltas.d.ts.map +1 -0
- package/dist/esm/react/deltas.js +267 -0
- package/dist/esm/react/deltas.js.map +1 -0
- package/dist/esm/react/index.d.ts +4 -49
- package/dist/esm/react/index.d.ts.map +1 -1
- package/dist/esm/react/index.js +15 -446
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/react/optimisticallySendMessage.d.ts +8 -0
- package/dist/esm/react/optimisticallySendMessage.d.ts.map +1 -0
- package/dist/esm/react/optimisticallySendMessage.js +40 -0
- package/dist/esm/react/optimisticallySendMessage.js.map +1 -0
- package/dist/esm/react/toUIMessages.d.ts +3 -3
- package/dist/esm/react/toUIMessages.d.ts.map +1 -1
- package/dist/esm/react/toUIMessages.js +4 -2
- package/dist/esm/react/toUIMessages.js.map +1 -1
- package/dist/esm/react/types.d.ts +26 -0
- package/dist/esm/react/types.d.ts.map +1 -0
- package/dist/esm/react/types.js +2 -0
- package/dist/esm/react/types.js.map +1 -0
- package/dist/esm/react/useSmoothText.d.ts +20 -0
- package/dist/esm/react/useSmoothText.d.ts.map +1 -0
- package/dist/esm/react/useSmoothText.js +50 -0
- package/dist/esm/react/useSmoothText.js.map +1 -0
- package/dist/esm/shared.d.ts +2 -2
- package/dist/esm/shared.d.ts.map +1 -1
- package/dist/esm/validators.d.ts +1 -1
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +2 -2
- package/dist/esm/validators.js.map +1 -1
- package/dist/esm.tsbuildinfo +1 -0
- package/package.json +5 -1
- package/src/client/createTool.ts +3 -5
- package/src/client/index.test.ts +1 -1
- package/src/client/index.ts +9 -10
- package/src/client/streaming.ts +15 -5
- package/src/component/files.ts +2 -2
- package/src/component/messages.test.ts +4 -4
- package/src/component/messages.ts +15 -15
- package/src/component/schema.ts +1 -1
- package/src/component/streams.ts +8 -9
- package/src/component/threads.ts +6 -6
- package/src/component/users.ts +7 -7
- package/src/component/vector/index.ts +12 -8
- package/src/component/vector/tables.ts +10 -10
- package/src/mapping.ts +4 -4
- package/src/react/deltas.test.ts +298 -0
- package/src/react/deltas.ts +340 -0
- package/src/react/index.ts +26 -589
- package/src/react/optimisticallySendMessage.ts +46 -0
- package/src/react/toUIMessages.test.ts +301 -0
- package/src/react/toUIMessages.ts +9 -5
- package/src/react/types.ts +52 -0
- package/src/react/useSmoothText.ts +76 -0
- package/src/shared.ts +2 -2
- package/src/validators.test.ts +22 -22
- package/src/validators.ts +8 -2
package/src/client/streaming.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type ChunkDetector,
|
|
3
|
+
smoothStream,
|
|
4
|
+
type StreamTextTransform,
|
|
5
|
+
type ToolSet,
|
|
6
|
+
} from "ai";
|
|
7
|
+
import type {
|
|
8
|
+
ProviderOptions,
|
|
9
|
+
StreamDelta,
|
|
10
|
+
TextStreamPart,
|
|
11
|
+
} from "../validators";
|
|
3
12
|
import type { AgentComponent, MessageDoc } from "./index";
|
|
4
|
-
import { RunActionCtx } from "./types";
|
|
13
|
+
import type { RunActionCtx } from "./types";
|
|
5
14
|
|
|
6
15
|
export type StreamingOptions = {
|
|
7
16
|
/**
|
|
@@ -9,7 +18,7 @@ export type StreamingOptions = {
|
|
|
9
18
|
* Note: this is not a guarantee that every delta will be exactly one line.
|
|
10
19
|
* E.g. if "line" is specified, it won't save any deltas until it encounters
|
|
11
20
|
* a newline character.
|
|
12
|
-
* Defaults to
|
|
21
|
+
* Defaults to a regex that chunks by punctuation followed by whitespace.
|
|
13
22
|
*/
|
|
14
23
|
chunking?: "word" | "line" | RegExp | ChunkDetector;
|
|
15
24
|
/**
|
|
@@ -19,7 +28,8 @@ export type StreamingOptions = {
|
|
|
19
28
|
throttleMs?: number;
|
|
20
29
|
};
|
|
21
30
|
export const DEFAULT_STREAMING_OPTIONS = {
|
|
22
|
-
|
|
31
|
+
// This chunks by sentences / clauses. Punctuation followed by whitespace.
|
|
32
|
+
chunking: /[\p{P}\s]/u,
|
|
23
33
|
throttleMs: 250,
|
|
24
34
|
} satisfies StreamingOptions;
|
|
25
35
|
|
package/src/component/files.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { paginator } from "convex-helpers/server/pagination";
|
|
2
|
-
import {
|
|
2
|
+
import type { Id } from "./_generated/dataModel.js";
|
|
3
|
+
import { mutation, type MutationCtx, query } from "./_generated/server.js";
|
|
3
4
|
import { schema, v } from "./schema.js";
|
|
4
|
-
import { Id } from "./_generated/dataModel.js";
|
|
5
5
|
|
|
6
6
|
export const addFile = mutation({
|
|
7
7
|
args: {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
|
-
import { describe, expect, test } from "vitest";
|
|
4
3
|
import { convexTest } from "convex-test";
|
|
5
|
-
import
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
6
5
|
import { api } from "./_generated/api.js";
|
|
7
|
-
import {
|
|
8
|
-
import { Id } from "./_generated/dataModel.js";
|
|
6
|
+
import type { Id } from "./_generated/dataModel.js";
|
|
9
7
|
import { getMaxMessage } from "./messages.js";
|
|
8
|
+
import schema from "./schema.js";
|
|
9
|
+
import { modules } from "./setup.test.js";
|
|
10
10
|
|
|
11
11
|
describe("agent", () => {
|
|
12
12
|
test("getMaxMessage works for threads", async () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert, omit } from "convex-helpers";
|
|
2
2
|
import { mergedStream, stream } from "convex-helpers/server/stream";
|
|
3
|
-
import {
|
|
3
|
+
import { paginationOptsValidator } from "convex/server";
|
|
4
|
+
import type { ObjectType } from "convex/values";
|
|
4
5
|
import {
|
|
5
6
|
DEFAULT_MESSAGE_RANGE,
|
|
6
7
|
DEFAULT_RECENT_MESSAGES,
|
|
@@ -8,38 +9,37 @@ import {
|
|
|
8
9
|
isTool,
|
|
9
10
|
} from "../shared.js";
|
|
10
11
|
import {
|
|
11
|
-
vPaginationResult,
|
|
12
12
|
vMessageEmbeddings,
|
|
13
13
|
vMessageStatus,
|
|
14
14
|
vMessageWithMetadata,
|
|
15
|
+
vPaginationResult,
|
|
15
16
|
vSearchOptions,
|
|
16
17
|
vStepWithMessages,
|
|
17
18
|
} from "../validators.js";
|
|
18
19
|
import { api, internal } from "./_generated/api.js";
|
|
19
|
-
import { Doc, Id } from "./_generated/dataModel.js";
|
|
20
|
+
import type { Doc, Id } from "./_generated/dataModel.js";
|
|
20
21
|
import {
|
|
21
22
|
action,
|
|
22
23
|
internalQuery,
|
|
23
24
|
mutation,
|
|
24
|
-
MutationCtx,
|
|
25
|
+
type MutationCtx,
|
|
25
26
|
query,
|
|
26
|
-
QueryCtx,
|
|
27
|
+
type QueryCtx,
|
|
27
28
|
} from "./_generated/server.js";
|
|
28
|
-
import {
|
|
29
|
+
import type { MessageDoc } from "./schema.js";
|
|
30
|
+
import { schema, v, vMessageDoc } from "./schema.js";
|
|
31
|
+
import {
|
|
32
|
+
getThread as _getThread,
|
|
33
|
+
listThreadsByUserId as _listThreadsByUserId,
|
|
34
|
+
updateThread as _updateThread,
|
|
35
|
+
} from "./threads.js";
|
|
29
36
|
import { insertVector, searchVectors } from "./vector/index.js";
|
|
30
37
|
import {
|
|
31
|
-
VectorDimension,
|
|
38
|
+
type VectorDimension,
|
|
32
39
|
VectorDimensions,
|
|
33
|
-
VectorTableId,
|
|
40
|
+
type VectorTableId,
|
|
34
41
|
vVectorId,
|
|
35
42
|
} from "./vector/tables.js";
|
|
36
|
-
import {
|
|
37
|
-
listThreadsByUserId as _listThreadsByUserId,
|
|
38
|
-
getThread as _getThread,
|
|
39
|
-
updateThread as _updateThread,
|
|
40
|
-
} from "./threads.js";
|
|
41
|
-
import { paginationOptsValidator } from "convex/server";
|
|
42
|
-
import { MessageDoc, vMessageDoc } from "./schema.js";
|
|
43
43
|
|
|
44
44
|
/** @deprecated Use *.threads.listMessagesByThreadId instead. */
|
|
45
45
|
export const listThreadsByUserId = _listThreadsByUserId;
|
package/src/component/schema.ts
CHANGED
package/src/component/streams.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { omit, pick } from "convex-helpers";
|
|
1
2
|
import { v } from "convex/values";
|
|
3
|
+
import { type StreamDelta, vStreamDelta, vStreamMessage } from "../validators";
|
|
4
|
+
import { internal } from "./_generated/api";
|
|
5
|
+
import type { Id } from "./_generated/dataModel";
|
|
2
6
|
import {
|
|
3
7
|
internalMutation,
|
|
4
8
|
mutation,
|
|
5
|
-
MutationCtx,
|
|
9
|
+
type MutationCtx,
|
|
6
10
|
query,
|
|
7
11
|
} from "./_generated/server";
|
|
8
|
-
import { omit, pick } from "convex-helpers";
|
|
9
|
-
import { StreamDelta, vStreamDelta, vStreamMessage } from "../validators";
|
|
10
12
|
import schema from "./schema";
|
|
11
|
-
import { internal } from "./_generated/api";
|
|
12
|
-
import { Id } from "./_generated/dataModel";
|
|
13
13
|
|
|
14
14
|
const MAX_DELTAS_PER_REQUEST = 1000;
|
|
15
15
|
const MAX_DELTAS_PER_STREAM = 100;
|
|
@@ -51,10 +51,9 @@ export const listDeltas = query({
|
|
|
51
51
|
);
|
|
52
52
|
totalDeltas += streamDeltas.length;
|
|
53
53
|
deltas.push(
|
|
54
|
-
...streamDeltas.map((d) =>
|
|
55
|
-
streamId
|
|
56
|
-
|
|
57
|
-
}))
|
|
54
|
+
...streamDeltas.map((d) =>
|
|
55
|
+
pick(d, ["streamId", "start", "end", "parts"])
|
|
56
|
+
)
|
|
58
57
|
);
|
|
59
58
|
if (totalDeltas >= MAX_DELTAS_PER_REQUEST) {
|
|
60
59
|
break;
|
package/src/component/threads.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { assert, omit, pick } from "convex-helpers";
|
|
2
2
|
import { paginator } from "convex-helpers/server/pagination";
|
|
3
3
|
import { partial } from "convex-helpers/validators";
|
|
4
|
-
import {
|
|
4
|
+
import { paginationOptsValidator } from "convex/server";
|
|
5
|
+
import type { ObjectType } from "convex/values";
|
|
6
|
+
import { type ThreadDoc, vThreadDoc } from "../client/index.js";
|
|
5
7
|
import { vPaginationResult } from "../validators.js";
|
|
6
8
|
import { api, internal } from "./_generated/api.js";
|
|
7
|
-
import { Doc } from "./_generated/dataModel.js";
|
|
9
|
+
import type { Doc } from "./_generated/dataModel.js";
|
|
8
10
|
import {
|
|
9
11
|
action,
|
|
10
12
|
internalMutation,
|
|
11
13
|
mutation,
|
|
12
|
-
MutationCtx,
|
|
14
|
+
type MutationCtx,
|
|
13
15
|
query,
|
|
14
16
|
} from "./_generated/server.js";
|
|
15
|
-
import { schema, v } from "./schema.js";
|
|
16
|
-
import { paginationOptsValidator } from "convex/server";
|
|
17
17
|
import { deleteMessage } from "./messages.js";
|
|
18
|
-
import {
|
|
18
|
+
import { schema, v } from "./schema.js";
|
|
19
19
|
|
|
20
20
|
function publicThreadOrNull(thread: Doc<"threads"> | null): ThreadDoc | null {
|
|
21
21
|
if (thread === null) {
|
package/src/component/users.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { paginator } from "convex-helpers/server/pagination";
|
|
2
|
+
import { stream } from "convex-helpers/server/stream";
|
|
2
3
|
import { nullable } from "convex-helpers/validators";
|
|
3
|
-
import {
|
|
4
|
+
import { paginationOptsValidator } from "convex/server";
|
|
5
|
+
import type { ObjectType } from "convex/values";
|
|
6
|
+
import { vPaginationResult } from "../validators.js";
|
|
4
7
|
import { internal } from "./_generated/api.js";
|
|
8
|
+
import type { Id } from "./_generated/dataModel.js";
|
|
5
9
|
import {
|
|
6
10
|
action,
|
|
7
11
|
internalMutation,
|
|
8
12
|
internalQuery,
|
|
9
13
|
mutation,
|
|
10
|
-
MutationCtx,
|
|
14
|
+
type MutationCtx,
|
|
11
15
|
query,
|
|
12
16
|
} from "./_generated/server.js";
|
|
13
|
-
import { schema, v } from "./schema.js";
|
|
14
17
|
import { deleteMessage } from "./messages.js";
|
|
15
|
-
import {
|
|
16
|
-
import { stream } from "convex-helpers/server/stream";
|
|
17
|
-
import { vPaginationResult } from "../validators.js";
|
|
18
|
-
import { Id } from "./_generated/dataModel.js";
|
|
18
|
+
import { schema, v } from "./schema.js";
|
|
19
19
|
|
|
20
20
|
// Note: it only searches for users with threads
|
|
21
21
|
export const listUsersWithThreads = query({
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { paginator } from "convex-helpers/server/pagination";
|
|
2
|
+
import { mergedStream, stream } from "convex-helpers/server/stream";
|
|
2
3
|
import { v } from "convex/values";
|
|
3
|
-
import {
|
|
4
|
+
import type { Id } from "../_generated/dataModel.js";
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
+
type ActionCtx,
|
|
7
|
+
mutation,
|
|
8
|
+
type MutationCtx,
|
|
9
|
+
query,
|
|
10
|
+
} from "../_generated/server.js";
|
|
11
|
+
import schema from "../schema.js";
|
|
12
|
+
import {
|
|
13
|
+
type EmbeddingsWithoutDenormalizedFields,
|
|
6
14
|
getVectorTableName,
|
|
7
|
-
VectorDimension,
|
|
15
|
+
type VectorDimension,
|
|
8
16
|
vEmbeddingsWithoutDenormalizedFields,
|
|
9
17
|
vVectorDimension,
|
|
10
18
|
vVectorId,
|
|
11
|
-
} from "./tables";
|
|
12
|
-
import schema from "../schema";
|
|
13
|
-
import { mergedStream } from "convex-helpers/server/stream";
|
|
14
|
-
import { stream } from "convex-helpers/server/stream";
|
|
15
|
-
import { Id } from "../_generated/dataModel";
|
|
19
|
+
} from "./tables.js";
|
|
16
20
|
|
|
17
21
|
export const paginate = query({
|
|
18
22
|
args: {
|
|
@@ -2,20 +2,20 @@ import { omit } from "convex-helpers";
|
|
|
2
2
|
import { literals } from "convex-helpers/validators";
|
|
3
3
|
import {
|
|
4
4
|
defineTable,
|
|
5
|
-
GenericTableSearchIndexes,
|
|
6
|
-
SchemaDefinition,
|
|
7
|
-
TableDefinition,
|
|
5
|
+
type GenericTableSearchIndexes,
|
|
6
|
+
type SchemaDefinition,
|
|
7
|
+
type TableDefinition,
|
|
8
8
|
} from "convex/server";
|
|
9
9
|
import {
|
|
10
|
-
GenericId,
|
|
11
|
-
Infer,
|
|
12
|
-
ObjectType,
|
|
10
|
+
type GenericId,
|
|
11
|
+
type Infer,
|
|
12
|
+
type ObjectType,
|
|
13
13
|
v,
|
|
14
|
-
VId,
|
|
15
|
-
VObject,
|
|
16
|
-
VUnion,
|
|
14
|
+
type VId,
|
|
15
|
+
type VObject,
|
|
16
|
+
type VUnion,
|
|
17
17
|
} from "convex/values";
|
|
18
|
-
import { QueryCtx } from "../_generated/server";
|
|
18
|
+
import type { QueryCtx } from "../_generated/server";
|
|
19
19
|
|
|
20
20
|
// We only generate embeddings for non-tool, non-system messages
|
|
21
21
|
const embeddings = {
|
package/src/mapping.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
import type { FileUIPart } from "@ai-sdk/ui-utils";
|
|
1
2
|
import {
|
|
2
3
|
convertToCoreMessages,
|
|
3
4
|
coreMessageSchema,
|
|
4
|
-
|
|
5
|
+
type Message as AIMessage,
|
|
5
6
|
type AssistantContent,
|
|
6
7
|
type CoreMessage,
|
|
7
8
|
type DataContent,
|
|
9
|
+
type GenerateObjectResult,
|
|
8
10
|
type StepResult,
|
|
9
11
|
type ToolContent,
|
|
10
12
|
type ToolSet,
|
|
11
|
-
type Message as AIMessage,
|
|
12
13
|
type UserContent,
|
|
13
14
|
} from "ai";
|
|
14
|
-
import type { FileUIPart } from "@ai-sdk/ui-utils";
|
|
15
15
|
import { assert } from "convex-helpers";
|
|
16
|
-
import {
|
|
16
|
+
import type {
|
|
17
17
|
MessageWithMetadata,
|
|
18
18
|
Step,
|
|
19
19
|
StepWithMessagesWithMetadata,
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mergeDeltas, applyDeltasToStreamMessage } from "./deltas";
|
|
3
|
+
import type { StreamMessage, StreamDelta, TextStreamPart } from "../validators";
|
|
4
|
+
|
|
5
|
+
function makeStreamMessage(
|
|
6
|
+
streamId: string,
|
|
7
|
+
order: number,
|
|
8
|
+
stepOrder: number
|
|
9
|
+
): StreamMessage {
|
|
10
|
+
return {
|
|
11
|
+
streamId,
|
|
12
|
+
order,
|
|
13
|
+
stepOrder,
|
|
14
|
+
} as StreamMessage;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function makeDelta(
|
|
18
|
+
streamId: string,
|
|
19
|
+
start: number,
|
|
20
|
+
end: number,
|
|
21
|
+
parts: TextStreamPart[]
|
|
22
|
+
): StreamDelta {
|
|
23
|
+
return {
|
|
24
|
+
streamId,
|
|
25
|
+
start,
|
|
26
|
+
end,
|
|
27
|
+
parts,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("mergeDeltas", () => {
|
|
32
|
+
it("merges a single text-delta into a message", () => {
|
|
33
|
+
const streamId = "s1";
|
|
34
|
+
const streamMessages = [makeStreamMessage(streamId, 1, 0)];
|
|
35
|
+
const deltas = [
|
|
36
|
+
makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
|
|
37
|
+
];
|
|
38
|
+
const [messages, newStreams, changed] = mergeDeltas(
|
|
39
|
+
"thread1",
|
|
40
|
+
streamMessages,
|
|
41
|
+
[],
|
|
42
|
+
deltas
|
|
43
|
+
);
|
|
44
|
+
expect(messages).toHaveLength(1);
|
|
45
|
+
expect(messages[0].text).toBe("Hello");
|
|
46
|
+
expect(messages[0].message?.role).toBe("assistant");
|
|
47
|
+
expect(changed).toBe(true);
|
|
48
|
+
expect(newStreams[0].cursor).toBe(5);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("merges multiple deltas for the same stream", () => {
|
|
52
|
+
const streamId = "s1";
|
|
53
|
+
const streamMessages = [makeStreamMessage(streamId, 1, 0)];
|
|
54
|
+
const deltas = [
|
|
55
|
+
makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
|
|
56
|
+
makeDelta(streamId, 5, 11, [
|
|
57
|
+
{ type: "text-delta", textDelta: " World!" },
|
|
58
|
+
]),
|
|
59
|
+
];
|
|
60
|
+
const [messages, newStreams, changed] = mergeDeltas(
|
|
61
|
+
"thread1",
|
|
62
|
+
streamMessages,
|
|
63
|
+
[],
|
|
64
|
+
deltas
|
|
65
|
+
);
|
|
66
|
+
expect(messages).toHaveLength(1);
|
|
67
|
+
expect(messages[0].text).toBe("Hello World!");
|
|
68
|
+
expect(changed).toBe(true);
|
|
69
|
+
expect(newStreams[0].cursor).toBe(11);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("handles tool-call and tool-result parts", () => {
|
|
73
|
+
const streamId = "s2";
|
|
74
|
+
const streamMessages = [makeStreamMessage(streamId, 2, 0)];
|
|
75
|
+
const deltas = [
|
|
76
|
+
makeDelta(streamId, 0, 1, [
|
|
77
|
+
{
|
|
78
|
+
type: "tool-call",
|
|
79
|
+
toolCallId: "call1",
|
|
80
|
+
toolName: "myTool",
|
|
81
|
+
args: "",
|
|
82
|
+
},
|
|
83
|
+
]),
|
|
84
|
+
makeDelta(streamId, 1, 2, [
|
|
85
|
+
{
|
|
86
|
+
type: "tool-result",
|
|
87
|
+
toolCallId: "call1",
|
|
88
|
+
toolName: "myTool",
|
|
89
|
+
result: "42",
|
|
90
|
+
},
|
|
91
|
+
]),
|
|
92
|
+
];
|
|
93
|
+
const [messages, _, changed] = mergeDeltas(
|
|
94
|
+
"thread1",
|
|
95
|
+
streamMessages,
|
|
96
|
+
[],
|
|
97
|
+
deltas
|
|
98
|
+
);
|
|
99
|
+
expect(messages).toHaveLength(2);
|
|
100
|
+
expect(messages[0].message?.role).toBe("assistant");
|
|
101
|
+
expect(messages[0].tool).toBe(true);
|
|
102
|
+
const content = messages[0].message?.content;
|
|
103
|
+
expect(content).toEqual([
|
|
104
|
+
{
|
|
105
|
+
type: "tool-call",
|
|
106
|
+
toolCallId: "call1",
|
|
107
|
+
toolName: "myTool",
|
|
108
|
+
args: "",
|
|
109
|
+
},
|
|
110
|
+
]);
|
|
111
|
+
expect(messages[1].message?.role).toBe("tool");
|
|
112
|
+
expect(messages[1].tool).toBe(true);
|
|
113
|
+
expect(messages[1].message?.content).toEqual([
|
|
114
|
+
{
|
|
115
|
+
type: "tool-result",
|
|
116
|
+
toolCallId: "call1",
|
|
117
|
+
toolName: "myTool",
|
|
118
|
+
result: "42",
|
|
119
|
+
},
|
|
120
|
+
]);
|
|
121
|
+
expect(changed).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("returns changed=false if no new deltas", () => {
|
|
125
|
+
const streamId = "s3";
|
|
126
|
+
const streamMessages = [makeStreamMessage(streamId, 3, 0)];
|
|
127
|
+
const deltas: StreamDelta[] = [];
|
|
128
|
+
const [messages, newStreams, changed] = mergeDeltas(
|
|
129
|
+
"thread1",
|
|
130
|
+
streamMessages,
|
|
131
|
+
[],
|
|
132
|
+
deltas
|
|
133
|
+
);
|
|
134
|
+
expect(messages).toHaveLength(0);
|
|
135
|
+
expect(changed).toBe(false);
|
|
136
|
+
expect(newStreams[0].cursor).toBe(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("handles multiple streams and sorts by order/stepOrder", () => {
|
|
140
|
+
const s1 = makeStreamMessage("s1", 1, 0);
|
|
141
|
+
const s2 = makeStreamMessage("s2", 2, 0);
|
|
142
|
+
const deltas = [
|
|
143
|
+
makeDelta("s2", 0, 3, [{ type: "text-delta", textDelta: "B" }]),
|
|
144
|
+
makeDelta("s1", 0, 3, [{ type: "text-delta", textDelta: "A" }]),
|
|
145
|
+
];
|
|
146
|
+
const [messages, _, changed] = mergeDeltas("thread1", [s2, s1], [], deltas);
|
|
147
|
+
expect(messages).toHaveLength(2);
|
|
148
|
+
expect(messages[0].text).toBe("A");
|
|
149
|
+
expect(messages[1].text).toBe("B");
|
|
150
|
+
expect(changed).toBe(true);
|
|
151
|
+
// Sorted by order
|
|
152
|
+
expect(messages[0].order).toBe(1);
|
|
153
|
+
expect(messages[1].order).toBe(2);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("does not duplicate text content when merging sequential text-deltas", () => {
|
|
157
|
+
const streamId = "s4";
|
|
158
|
+
const streamMessages = [makeStreamMessage(streamId, 4, 0)];
|
|
159
|
+
const deltas = [
|
|
160
|
+
makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
|
|
161
|
+
makeDelta(streamId, 5, 11, [
|
|
162
|
+
{ type: "text-delta", textDelta: " World!" },
|
|
163
|
+
]),
|
|
164
|
+
makeDelta(streamId, 11, 12, [{ type: "text-delta", textDelta: "!" }]),
|
|
165
|
+
];
|
|
166
|
+
const [messages] = mergeDeltas("thread1", streamMessages, [], deltas);
|
|
167
|
+
expect(messages).toHaveLength(1);
|
|
168
|
+
expect(messages[0].text).toBe("Hello World!!");
|
|
169
|
+
// There should only be one text part per message
|
|
170
|
+
const content = messages[0].message?.content;
|
|
171
|
+
if (Array.isArray(content)) {
|
|
172
|
+
const textParts = content.filter((p) => p.type === "text");
|
|
173
|
+
expect(textParts).toHaveLength(1);
|
|
174
|
+
expect(textParts[0].text).toBe("Hello World!!");
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("does not duplicate reasoning parts", () => {
|
|
179
|
+
const streamId = "s6";
|
|
180
|
+
const streamMessages = [makeStreamMessage(streamId, 6, 0)];
|
|
181
|
+
const deltas = [
|
|
182
|
+
makeDelta(streamId, 0, 1, [
|
|
183
|
+
{ type: "reasoning", textDelta: "I'm thinking..." },
|
|
184
|
+
]),
|
|
185
|
+
makeDelta(streamId, 1, 2, [
|
|
186
|
+
{ type: "reasoning", textDelta: " Still thinking..." },
|
|
187
|
+
]),
|
|
188
|
+
];
|
|
189
|
+
const [messages] = mergeDeltas("thread1", streamMessages, [], deltas);
|
|
190
|
+
expect(messages).toHaveLength(1);
|
|
191
|
+
if (Array.isArray(messages[0].message?.content)) {
|
|
192
|
+
const reasoningParts = messages[0].message.content.filter(
|
|
193
|
+
(p) => p.type === "reasoning"
|
|
194
|
+
);
|
|
195
|
+
expect(reasoningParts).toHaveLength(1);
|
|
196
|
+
expect(reasoningParts[0].text).toBe("I'm thinking... Still thinking...");
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("applyDeltasToStreamMessage is idempotent and does not duplicate content", () => {
|
|
201
|
+
const streamId = "s7";
|
|
202
|
+
const streamMessage = makeStreamMessage(streamId, 7, 0);
|
|
203
|
+
const deltas = [
|
|
204
|
+
makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
|
|
205
|
+
makeDelta(streamId, 5, 11, [
|
|
206
|
+
{ type: "text-delta", textDelta: " World!" },
|
|
207
|
+
]),
|
|
208
|
+
];
|
|
209
|
+
// First call: apply both deltas
|
|
210
|
+
let [result, changed] = applyDeltasToStreamMessage(
|
|
211
|
+
"thread1",
|
|
212
|
+
streamMessage,
|
|
213
|
+
undefined,
|
|
214
|
+
deltas
|
|
215
|
+
);
|
|
216
|
+
expect(result.messages).toHaveLength(1);
|
|
217
|
+
expect(result.messages[0].text).toBe("Hello World!");
|
|
218
|
+
// Second call: re-apply the same deltas (should not duplicate)
|
|
219
|
+
[result, changed] = applyDeltasToStreamMessage(
|
|
220
|
+
"thread1",
|
|
221
|
+
streamMessage,
|
|
222
|
+
result,
|
|
223
|
+
deltas
|
|
224
|
+
);
|
|
225
|
+
expect(result.messages).toHaveLength(1);
|
|
226
|
+
expect(result.messages[0].text).toBe("Hello World!");
|
|
227
|
+
// Third call: add a new delta
|
|
228
|
+
const moreDeltas = [
|
|
229
|
+
...deltas,
|
|
230
|
+
makeDelta(streamId, 11, 12, [{ type: "text-delta", textDelta: "!" }]),
|
|
231
|
+
];
|
|
232
|
+
[result, changed] = applyDeltasToStreamMessage(
|
|
233
|
+
"thread1",
|
|
234
|
+
streamMessage,
|
|
235
|
+
result,
|
|
236
|
+
moreDeltas
|
|
237
|
+
);
|
|
238
|
+
expect(changed).toBe(true);
|
|
239
|
+
expect(result.messages).toHaveLength(1);
|
|
240
|
+
expect(result.messages[0].text).toBe("Hello World!!");
|
|
241
|
+
// Re-apply all deltas again (should still not duplicate)
|
|
242
|
+
[result, changed] = applyDeltasToStreamMessage(
|
|
243
|
+
"thread1",
|
|
244
|
+
streamMessage,
|
|
245
|
+
result,
|
|
246
|
+
moreDeltas
|
|
247
|
+
);
|
|
248
|
+
expect(changed).toBe(false);
|
|
249
|
+
expect(result.messages).toHaveLength(1);
|
|
250
|
+
expect(result.messages[0].text).toBe("Hello World!!");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("mergeDeltas is pure and does not mutate inputs", () => {
|
|
254
|
+
const streamId = "s8";
|
|
255
|
+
const streamMessages = [makeStreamMessage(streamId, 8, 0)];
|
|
256
|
+
const deltas = [
|
|
257
|
+
makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
|
|
258
|
+
makeDelta(streamId, 5, 11, [
|
|
259
|
+
{ type: "text-delta", textDelta: " World!" },
|
|
260
|
+
]),
|
|
261
|
+
];
|
|
262
|
+
// Deep freeze inputs to catch mutation
|
|
263
|
+
function deepFreeze(obj: unknown): unknown {
|
|
264
|
+
if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
|
|
265
|
+
Object.freeze(obj);
|
|
266
|
+
for (const key of Object.keys(obj)) {
|
|
267
|
+
deepFreeze((obj as Record<string, unknown>)[key]);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return obj;
|
|
271
|
+
}
|
|
272
|
+
deepFreeze(streamMessages);
|
|
273
|
+
deepFreeze(deltas);
|
|
274
|
+
const [messages1, streams1, changed1] = mergeDeltas(
|
|
275
|
+
"thread1",
|
|
276
|
+
streamMessages,
|
|
277
|
+
[],
|
|
278
|
+
deltas
|
|
279
|
+
);
|
|
280
|
+
const [messages2, streams2, changed2] = mergeDeltas(
|
|
281
|
+
"thread1",
|
|
282
|
+
streamMessages,
|
|
283
|
+
[],
|
|
284
|
+
deltas
|
|
285
|
+
);
|
|
286
|
+
expect(messages1).toEqual(messages2);
|
|
287
|
+
expect(streams1).toEqual(streams2);
|
|
288
|
+
expect(changed1).toBe(changed2);
|
|
289
|
+
// Inputs should remain unchanged
|
|
290
|
+
expect(streamMessages).toEqual([makeStreamMessage(streamId, 8, 0)]);
|
|
291
|
+
expect(deltas).toEqual([
|
|
292
|
+
makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
|
|
293
|
+
makeDelta(streamId, 5, 11, [
|
|
294
|
+
{ type: "text-delta", textDelta: " World!" },
|
|
295
|
+
]),
|
|
296
|
+
]);
|
|
297
|
+
});
|
|
298
|
+
});
|