convex-langfuse 0.0.1

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.
Files changed (52) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +334 -0
  3. package/dist/client/_generated/_ignore.d.ts +1 -0
  4. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  5. package/dist/client/_generated/_ignore.js +3 -0
  6. package/dist/client/_generated/_ignore.js.map +1 -0
  7. package/dist/client/index.d.ts +166 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +223 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/component/_generated/api.d.ts +34 -0
  12. package/dist/component/_generated/api.d.ts.map +1 -0
  13. package/dist/component/_generated/api.js +31 -0
  14. package/dist/component/_generated/api.js.map +1 -0
  15. package/dist/component/_generated/component.d.ts +157 -0
  16. package/dist/component/_generated/component.d.ts.map +1 -0
  17. package/dist/component/_generated/component.js +11 -0
  18. package/dist/component/_generated/component.js.map +1 -0
  19. package/dist/component/_generated/dataModel.d.ts +46 -0
  20. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  21. package/dist/component/_generated/dataModel.js +11 -0
  22. package/dist/component/_generated/dataModel.js.map +1 -0
  23. package/dist/component/_generated/server.d.ts +133 -0
  24. package/dist/component/_generated/server.d.ts.map +1 -0
  25. package/dist/component/_generated/server.js +80 -0
  26. package/dist/component/_generated/server.js.map +1 -0
  27. package/dist/component/convex.config.d.ts +3 -0
  28. package/dist/component/convex.config.d.ts.map +1 -0
  29. package/dist/component/convex.config.js +4 -0
  30. package/dist/component/convex.config.js.map +1 -0
  31. package/dist/component/lib.d.ts +133 -0
  32. package/dist/component/lib.d.ts.map +1 -0
  33. package/dist/component/lib.js +214 -0
  34. package/dist/component/lib.js.map +1 -0
  35. package/dist/component/schema.d.ts +83 -0
  36. package/dist/component/schema.d.ts.map +1 -0
  37. package/dist/component/schema.js +48 -0
  38. package/dist/component/schema.js.map +1 -0
  39. package/package.json +106 -0
  40. package/src/client/_generated/_ignore.ts +1 -0
  41. package/src/client/index.ts +322 -0
  42. package/src/client/setup.test.ts +26 -0
  43. package/src/component/_generated/api.ts +50 -0
  44. package/src/component/_generated/component.ts +222 -0
  45. package/src/component/_generated/dataModel.ts +60 -0
  46. package/src/component/_generated/server.ts +169 -0
  47. package/src/component/convex.config.ts +5 -0
  48. package/src/component/lib.test.ts +130 -0
  49. package/src/component/lib.ts +252 -0
  50. package/src/component/schema.ts +66 -0
  51. package/src/component/setup.test.ts +11 -0
  52. package/src/test.ts +18 -0
@@ -0,0 +1,252 @@
1
+ import { v } from "convex/values";
2
+ import { mutation, query } from "./_generated/server.js";
3
+
4
+ const observationTypeValidator = v.union(
5
+ v.literal("span"),
6
+ v.literal("generation"),
7
+ v.literal("event"),
8
+ v.literal("embedding"),
9
+ v.literal("agent"),
10
+ v.literal("tool"),
11
+ v.literal("chain"),
12
+ v.literal("retriever"),
13
+ v.literal("guardrail"),
14
+ v.literal("evaluator"),
15
+ );
16
+
17
+ const levelValidator = v.union(
18
+ v.literal("DEBUG"),
19
+ v.literal("DEFAULT"),
20
+ v.literal("WARNING"),
21
+ v.literal("ERROR"),
22
+ );
23
+
24
+ const traceValidator = v.object({
25
+ _id: v.id("traces"),
26
+ _creationTime: v.number(),
27
+ traceId: v.string(),
28
+ name: v.string(),
29
+ userId: v.optional(v.string()),
30
+ sessionId: v.optional(v.string()),
31
+ tags: v.optional(v.array(v.string())),
32
+ metadata: v.optional(v.string()),
33
+ release: v.optional(v.string()),
34
+ createdAt: v.number(),
35
+ updatedAt: v.number(),
36
+ });
37
+
38
+ const observationValidator = v.object({
39
+ _id: v.id("observations"),
40
+ _creationTime: v.number(),
41
+ observationId: v.string(),
42
+ traceId: v.string(),
43
+ type: observationTypeValidator,
44
+ name: v.string(),
45
+ input: v.optional(v.string()),
46
+ output: v.optional(v.string()),
47
+ model: v.optional(v.string()),
48
+ inputTokens: v.optional(v.number()),
49
+ outputTokens: v.optional(v.number()),
50
+ level: levelValidator,
51
+ statusMessage: v.optional(v.string()),
52
+ metadata: v.optional(v.string()),
53
+ startedAt: v.number(),
54
+ endedAt: v.number(),
55
+ createdAt: v.number(),
56
+ });
57
+
58
+ const scoreValidator = v.object({
59
+ _id: v.id("scores"),
60
+ _creationTime: v.number(),
61
+ scoreId: v.string(),
62
+ traceId: v.string(),
63
+ name: v.string(),
64
+ value: v.number(),
65
+ comment: v.optional(v.string()),
66
+ createdAt: v.number(),
67
+ });
68
+
69
+ // ─── Queries ────────────────────────────────────────────────────────────────
70
+
71
+ export const getTrace = query({
72
+ args: { traceId: v.string() },
73
+ returns: v.union(v.null(), traceValidator),
74
+ handler: async (ctx, args) => {
75
+ return await ctx.db
76
+ .query("traces")
77
+ .withIndex("by_traceId", (q) => q.eq("traceId", args.traceId))
78
+ .first();
79
+ },
80
+ });
81
+
82
+ export const listTracesByUser = query({
83
+ args: { userId: v.string(), limit: v.optional(v.number()) },
84
+ returns: v.array(traceValidator),
85
+ handler: async (ctx, args) => {
86
+ return await ctx.db
87
+ .query("traces")
88
+ .withIndex("by_userId", (q) => q.eq("userId", args.userId))
89
+ .order("desc")
90
+ .take(args.limit ?? 50);
91
+ },
92
+ });
93
+
94
+ export const listTracesBySession = query({
95
+ args: { sessionId: v.string(), limit: v.optional(v.number()) },
96
+ returns: v.array(traceValidator),
97
+ handler: async (ctx, args) => {
98
+ return await ctx.db
99
+ .query("traces")
100
+ .withIndex("by_sessionId", (q) => q.eq("sessionId", args.sessionId))
101
+ .order("desc")
102
+ .take(args.limit ?? 50);
103
+ },
104
+ });
105
+
106
+ export const listObservations = query({
107
+ args: { traceId: v.string() },
108
+ returns: v.array(observationValidator),
109
+ handler: async (ctx, args) => {
110
+ return await ctx.db
111
+ .query("observations")
112
+ .withIndex("by_traceId", (q) => q.eq("traceId", args.traceId))
113
+ .order("asc")
114
+ .collect();
115
+ },
116
+ });
117
+
118
+ export const listScores = query({
119
+ args: { traceId: v.string() },
120
+ returns: v.array(scoreValidator),
121
+ handler: async (ctx, args) => {
122
+ return await ctx.db
123
+ .query("scores")
124
+ .withIndex("by_traceId", (q) => q.eq("traceId", args.traceId))
125
+ .collect();
126
+ },
127
+ });
128
+
129
+ // Every trace, newest first, regardless of which user/session it belongs to.
130
+ // Unlike listTracesByUser/listTracesBySession, this doesn't require already
131
+ // knowing an identity to look under — useful for a "history" view where the
132
+ // caller's own userId/sessionId is only a client-side value that doesn't
133
+ // survive a page reload.
134
+ export const listRecentTraces = query({
135
+ args: { limit: v.optional(v.number()) },
136
+ returns: v.array(traceValidator),
137
+ handler: async (ctx, args) => {
138
+ return await ctx.db
139
+ .query("traces")
140
+ .order("desc")
141
+ .take(args.limit ?? 50);
142
+ },
143
+ });
144
+
145
+ export const getStats = query({
146
+ args: {},
147
+ returns: v.object({ traces: v.number(), observations: v.number(), scores: v.number() }),
148
+ handler: async (ctx) => {
149
+ const [traces, observations, scores] = await Promise.all([
150
+ ctx.db.query("traces").collect(),
151
+ ctx.db.query("observations").collect(),
152
+ ctx.db.query("scores").collect(),
153
+ ]);
154
+ return { traces: traces.length, observations: observations.length, scores: scores.length };
155
+ },
156
+ });
157
+
158
+ // ─── Mutations ──────────────────────────────────────────────────────────────
159
+
160
+ export const recordTrace = mutation({
161
+ args: {
162
+ traceId: v.string(),
163
+ name: v.string(),
164
+ userId: v.optional(v.string()),
165
+ sessionId: v.optional(v.string()),
166
+ tags: v.optional(v.array(v.string())),
167
+ metadata: v.optional(v.string()),
168
+ release: v.optional(v.string()),
169
+ },
170
+ returns: v.id("traces"),
171
+ handler: async (ctx, args) => {
172
+ const now = Date.now();
173
+ const existing = await ctx.db
174
+ .query("traces")
175
+ .withIndex("by_traceId", (q) => q.eq("traceId", args.traceId))
176
+ .first();
177
+
178
+ if (existing) {
179
+ await ctx.db.patch(existing._id, { ...args, updatedAt: now });
180
+ return existing._id;
181
+ }
182
+
183
+ return await ctx.db.insert("traces", {
184
+ ...args,
185
+ createdAt: now,
186
+ updatedAt: now,
187
+ });
188
+ },
189
+ });
190
+
191
+ export const recordObservation = mutation({
192
+ args: {
193
+ observationId: v.string(),
194
+ traceId: v.string(),
195
+ type: observationTypeValidator,
196
+ name: v.string(),
197
+ input: v.optional(v.string()),
198
+ output: v.optional(v.string()),
199
+ model: v.optional(v.string()),
200
+ inputTokens: v.optional(v.number()),
201
+ outputTokens: v.optional(v.number()),
202
+ level: levelValidator,
203
+ statusMessage: v.optional(v.string()),
204
+ metadata: v.optional(v.string()),
205
+ startedAt: v.number(),
206
+ endedAt: v.number(),
207
+ },
208
+ returns: v.id("observations"),
209
+ handler: async (ctx, args) => {
210
+ const existing = await ctx.db
211
+ .query("observations")
212
+ .withIndex("by_observationId", (q) => q.eq("observationId", args.observationId))
213
+ .first();
214
+
215
+ if (existing) {
216
+ await ctx.db.patch(existing._id, args);
217
+ return existing._id;
218
+ }
219
+
220
+ return await ctx.db.insert("observations", {
221
+ ...args,
222
+ createdAt: Date.now(),
223
+ });
224
+ },
225
+ });
226
+
227
+ export const recordScore = mutation({
228
+ args: {
229
+ scoreId: v.string(),
230
+ traceId: v.string(),
231
+ name: v.string(),
232
+ value: v.number(),
233
+ comment: v.optional(v.string()),
234
+ },
235
+ returns: v.id("scores"),
236
+ handler: async (ctx, args) => {
237
+ const existing = await ctx.db
238
+ .query("scores")
239
+ .withIndex("by_scoreId", (q) => q.eq("scoreId", args.scoreId))
240
+ .first();
241
+
242
+ if (existing) {
243
+ await ctx.db.patch(existing._id, args);
244
+ return existing._id;
245
+ }
246
+
247
+ return await ctx.db.insert("scores", {
248
+ ...args,
249
+ createdAt: Date.now(),
250
+ });
251
+ },
252
+ });
@@ -0,0 +1,66 @@
1
+ import { defineSchema, defineTable } from "convex/server";
2
+ import { v } from "convex/values";
3
+
4
+ export default defineSchema({
5
+ traces: defineTable({
6
+ traceId: v.string(),
7
+ name: v.string(),
8
+ userId: v.optional(v.string()),
9
+ sessionId: v.optional(v.string()),
10
+ tags: v.optional(v.array(v.string())),
11
+ metadata: v.optional(v.string()),
12
+ release: v.optional(v.string()),
13
+ createdAt: v.number(),
14
+ updatedAt: v.number(),
15
+ })
16
+ .index("by_traceId", ["traceId"])
17
+ .index("by_userId", ["userId"])
18
+ .index("by_sessionId", ["sessionId"]),
19
+
20
+ observations: defineTable({
21
+ observationId: v.string(),
22
+ traceId: v.string(),
23
+ type: v.union(
24
+ v.literal("span"),
25
+ v.literal("generation"),
26
+ v.literal("event"),
27
+ v.literal("embedding"),
28
+ v.literal("agent"),
29
+ v.literal("tool"),
30
+ v.literal("chain"),
31
+ v.literal("retriever"),
32
+ v.literal("guardrail"),
33
+ v.literal("evaluator"),
34
+ ),
35
+ name: v.string(),
36
+ input: v.optional(v.string()),
37
+ output: v.optional(v.string()),
38
+ model: v.optional(v.string()),
39
+ inputTokens: v.optional(v.number()),
40
+ outputTokens: v.optional(v.number()),
41
+ level: v.union(
42
+ v.literal("DEBUG"),
43
+ v.literal("DEFAULT"),
44
+ v.literal("WARNING"),
45
+ v.literal("ERROR"),
46
+ ),
47
+ statusMessage: v.optional(v.string()),
48
+ metadata: v.optional(v.string()),
49
+ startedAt: v.number(),
50
+ endedAt: v.number(),
51
+ createdAt: v.number(),
52
+ })
53
+ .index("by_observationId", ["observationId"])
54
+ .index("by_traceId", ["traceId"]),
55
+
56
+ scores: defineTable({
57
+ scoreId: v.string(),
58
+ traceId: v.string(),
59
+ name: v.string(),
60
+ value: v.number(),
61
+ comment: v.optional(v.string()),
62
+ createdAt: v.number(),
63
+ })
64
+ .index("by_scoreId", ["scoreId"])
65
+ .index("by_traceId", ["traceId"]),
66
+ });
@@ -0,0 +1,11 @@
1
+ /// <reference types="vite/client" />
2
+ import { test } from "vitest";
3
+ import schema from "./schema.js";
4
+ import { convexTest } from "convex-test";
5
+ export const modules = import.meta.glob("./**/*.*s");
6
+
7
+ export function initConvexTest() {
8
+ const t = convexTest(schema, modules);
9
+ return t;
10
+ }
11
+ test("setup", () => {});
package/src/test.ts ADDED
@@ -0,0 +1,18 @@
1
+ /// <reference types="vite/client" />
2
+ import type { TestConvex } from "convex-test";
3
+ import type { GenericSchema, SchemaDefinition } from "convex/server";
4
+ import schema from "./component/schema.js";
5
+ const modules = import.meta.glob("./component/**/*.ts");
6
+
7
+ /**
8
+ * Register the component with the test convex instance.
9
+ * @param t - The test convex instance, e.g. from calling `convexTest`.
10
+ * @param name - The name of the component, as registered in convex.config.ts.
11
+ */
12
+ export function register(
13
+ t: TestConvex<SchemaDefinition<GenericSchema, boolean>>,
14
+ name: string = "convexLangfuse",
15
+ ) {
16
+ t.registerComponent(name, schema, modules);
17
+ }
18
+ export default { register, schema, modules };