@voltro/ai 0.85.0 → 0.86.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +666 -0
  2. package/THIRD-PARTY-NOTICES.md +5 -33
  3. package/dist/agent.js +64 -2
  4. package/dist/agentConstants.js +4 -0
  5. package/dist/agentExecutor.js +51 -0
  6. package/dist/api.js +199 -0
  7. package/dist/appTools.js +80 -0
  8. package/dist/budget.js +201 -0
  9. package/dist/callEvidence.js +51 -0
  10. package/dist/copilot.js +126 -0
  11. package/dist/cost.js +242 -0
  12. package/dist/embed.js +41 -0
  13. package/dist/embeddingProvider.js +51 -0
  14. package/dist/eval.js +130 -0
  15. package/dist/events.d.ts +4 -0
  16. package/dist/events.js +81 -2
  17. package/dist/executableModalities.js +13 -0
  18. package/dist/gateway.js +58 -0
  19. package/dist/gatewayCatalog.js +47 -0
  20. package/dist/gatewayMedia.js +88 -0
  21. package/dist/index.d.ts +11 -0
  22. package/dist/index.js +42 -2750
  23. package/dist/inferenceDispatch.js +493 -0
  24. package/dist/inferenceQueue.js +96 -0
  25. package/dist/jsonOutputValidation.js +27 -0
  26. package/dist/mcpClient.js +332 -0
  27. package/dist/mcpTools.js +80 -0
  28. package/dist/media.js +182 -0
  29. package/dist/mediaBytes.js +19 -0
  30. package/dist/mediaCall.js +13 -0
  31. package/dist/mediaTaskProvider.js +16 -0
  32. package/dist/middleware.js +32 -0
  33. package/dist/observe.js +52 -0
  34. package/dist/prompts.js +103 -0
  35. package/dist/provider.js +303 -0
  36. package/dist/realtimeEvent.js +97 -0
  37. package/dist/realtimeSession.js +154 -0
  38. package/dist/rerank.js +61 -0
  39. package/dist/resumableStream.js +321 -0
  40. package/dist/retrieveReranked.js +16 -0
  41. package/dist/runAndPersist.js +148 -0
  42. package/dist/semanticCache.js +66 -0
  43. package/dist/speechProvider.js +16 -0
  44. package/dist/streamRegistry.js +14 -0
  45. package/dist/streamText.js +170 -0
  46. package/dist/streamTranscribe.js +74 -0
  47. package/dist/streamWakeups.js +44 -0
  48. package/dist/structuredEvidence.js +48 -0
  49. package/dist/test.js +1 -1
  50. package/dist/threads.js +92 -0
  51. package/dist/tools.js +68 -0
  52. package/dist/transcribe.js +64 -0
  53. package/dist/transcriptionEvent.js +46 -0
  54. package/dist/workflow.d.ts +2 -0
  55. package/dist/workflow.js +115 -110
  56. package/package.json +5 -5
  57. package/dist/agentDescriptor-DhoiXVqO.js +0 -63
  58. package/dist/agentEvent-Bl4egdM5.js +0 -211
  59. package/dist/inferenceQueue-DcEulZep.js +0 -1077
  60. package/dist/provider-BnemgE4D.js +0 -409
@@ -0,0 +1,51 @@
1
+ import { gatewayCostUsd as e } from "./cost.js";
2
+ import { Schema as t } from "effect";
3
+ //#region src/callEvidence.ts
4
+ var n = t.Number.pipe(t.check(t.isInt()), t.check(t.isGreaterThanOrEqualTo(0)), t.check(t.isLessThanOrEqualTo(2 ** 53 - 1))), r = t.Struct({
5
+ provider: t.String,
6
+ model: t.String,
7
+ usage: t.Struct({
8
+ inputTokens: t.optional(n),
9
+ outputTokens: t.optional(n),
10
+ totalTokens: t.optional(n),
11
+ completeness: t.Literals([
12
+ "complete",
13
+ "partial",
14
+ "unknown"
15
+ ])
16
+ }),
17
+ cost: t.Union([t.Struct({
18
+ status: t.Literal("actual"),
19
+ source: t.Literal("gateway"),
20
+ usd: t.Number.pipe(t.check(t.isFinite()), t.check(t.isGreaterThanOrEqualTo(0)))
21
+ }), t.Struct({
22
+ status: t.Literal("unknown"),
23
+ source: t.Literal("unreported")
24
+ })])
25
+ }), i = (e) => typeof e == "number" && Number.isSafeInteger(e) && e >= 0 ? e : void 0, a = (t, n, r, a) => {
26
+ let o = typeof r == "object" && r ? r : {}, s = i("inputTokens" in o ? o.inputTokens : void 0), c = i("outputTokens" in o ? o.outputTokens : void 0), l = i("totalTokens" in o ? o.totalTokens : void 0), u = [
27
+ s,
28
+ c,
29
+ l
30
+ ].filter((e) => e !== void 0).length, d = e(a);
31
+ return {
32
+ provider: t,
33
+ model: n,
34
+ usage: {
35
+ ...s === void 0 ? {} : { inputTokens: s },
36
+ ...c === void 0 ? {} : { outputTokens: c },
37
+ ...l === void 0 ? {} : { totalTokens: l },
38
+ completeness: u === 3 ? "complete" : u === 0 ? "unknown" : "partial"
39
+ },
40
+ cost: d === void 0 ? {
41
+ status: "unknown",
42
+ source: "unreported"
43
+ } : {
44
+ status: "actual",
45
+ source: "gateway",
46
+ usd: d
47
+ }
48
+ };
49
+ };
50
+ //#endregion
51
+ export { r as AiCallEvidenceSchema, a as normalizeAiCallEvidence };
@@ -0,0 +1,126 @@
1
+ import { Schema as e } from "effect";
2
+ import { and as t, contains as n, eq as r, gt as i, gte as a, inSet as o, lt as s, lte as c, neq as l } from "@voltro/database";
3
+ //#region src/copilot.ts
4
+ var u = class extends e.TaggedError()("CopilotRejected", {
5
+ reason: e.Literals([
6
+ "unknown-table",
7
+ "unknown-column",
8
+ "bad-op",
9
+ "bad-aggregate",
10
+ "empty"
11
+ ]),
12
+ detail: e.String
13
+ }) {}, d = 200, f = 50, p = /* @__PURE__ */ new Set([
14
+ "eq",
15
+ "neq",
16
+ "gt",
17
+ "gte",
18
+ "lt",
19
+ "lte",
20
+ "contains",
21
+ "in"
22
+ ]), m = (e) => {
23
+ switch (e.op) {
24
+ case "eq": return r(e.column, e.value);
25
+ case "neq": return l(e.column, e.value);
26
+ case "gt": return i(e.column, e.value);
27
+ case "gte": return a(e.column, e.value);
28
+ case "lt": return s(e.column, e.value);
29
+ case "lte": return c(e.column, e.value);
30
+ case "contains": return n(e.column, e.value);
31
+ case "in": return o(e.column, Array.isArray(e.value) ? e.value : [e.value]);
32
+ }
33
+ }, h = (e, t) => ({
34
+ ok: !1,
35
+ rejection: new u({
36
+ reason: e,
37
+ detail: t
38
+ })
39
+ }), g = (e, n) => {
40
+ let r = n.tables.find((t) => t.name === e.table);
41
+ if (r === void 0) return h("unknown-table", e.table);
42
+ let i = new Set(r.columns.map((e) => e.name));
43
+ for (let t of e.columns ?? []) if (!i.has(t)) return h("unknown-column", `${e.table}.${t}`);
44
+ for (let t of e.filters ?? []) {
45
+ if (!p.has(t.op)) return h("bad-op", String(t.op));
46
+ if (!i.has(t.column)) return h("unknown-column", `${e.table}.${t.column}`);
47
+ }
48
+ if (e.orderBy !== void 0 && !i.has(e.orderBy.column)) return h("unknown-column", `${e.table}.${e.orderBy.column}`);
49
+ if (e.aggregate !== void 0) {
50
+ let { fn: t, column: n } = e.aggregate;
51
+ if (t !== "count" && (n === void 0 || !i.has(n))) return h("bad-aggregate", `${t}(${n ?? "∅"})`);
52
+ }
53
+ let a = e.filters ?? [], o = a.length === 0 ? void 0 : a.length === 1 ? m(a[0]) : t(...a.map(m)), s = Math.min(Math.max(1, e.limit ?? f), 200), c = {
54
+ table: e.table,
55
+ predicate: o,
56
+ order: e.orderBy ? [{
57
+ column: e.orderBy.column,
58
+ direction: e.orderBy.direction ?? "asc"
59
+ }] : [],
60
+ take: s,
61
+ skip: void 0,
62
+ projection: e.columns && e.columns.length > 0 ? [...e.columns] : void 0
63
+ };
64
+ return e.aggregate ? {
65
+ ok: !0,
66
+ descriptor: c,
67
+ table: e.table,
68
+ aggregate: e.aggregate
69
+ } : {
70
+ ok: !0,
71
+ descriptor: c,
72
+ table: e.table
73
+ };
74
+ }, _ = e.Struct({
75
+ table: e.String,
76
+ columns: e.optional(e.Array(e.String)),
77
+ filters: e.optional(e.Array(e.Struct({
78
+ column: e.String,
79
+ op: e.Literals([
80
+ "eq",
81
+ "neq",
82
+ "gt",
83
+ "gte",
84
+ "lt",
85
+ "lte",
86
+ "contains",
87
+ "in"
88
+ ]),
89
+ value: e.Unknown
90
+ }))),
91
+ orderBy: e.optional(e.Struct({
92
+ column: e.String,
93
+ direction: e.optional(e.Literals(["asc", "desc"]))
94
+ })),
95
+ limit: e.optional(e.Number),
96
+ aggregate: e.optional(e.Struct({
97
+ fn: e.Literals([
98
+ "count",
99
+ "sum",
100
+ "avg",
101
+ "min",
102
+ "max"
103
+ ]),
104
+ column: e.optional(e.String)
105
+ }))
106
+ }), v = (e) => [
107
+ "You are a data copilot. Translate the user question into a JSON query proposal.",
108
+ "",
109
+ "You may ONLY reference these tables and columns:",
110
+ e.tables.map((e) => `- ${e.name}(${e.columns.map((e) => e.name).join(", ")})`).join("\n"),
111
+ "",
112
+ "Rules:",
113
+ "- Pick EXACTLY ONE table from the list as `table`.",
114
+ "- Every column in `columns`, `filters`, `orderBy`, and `aggregate` MUST be one of that table's listed columns.",
115
+ "- NEVER invent a table or column. If the question cannot be answered from the listed schema, pick the closest table and leave filters empty.",
116
+ "- The query is READ-ONLY. You cannot insert, update, or delete.",
117
+ "- Keep the result small (limit ≤ 200)."
118
+ ].join("\n"), y = async (e, t, n) => {
119
+ let r = v(t);
120
+ return g(await n.propose({
121
+ system: r,
122
+ prompt: e
123
+ }), t);
124
+ };
125
+ //#endregion
126
+ export { d as COPILOT_MAX_LIMIT, _ as CopilotProposalSchema, u as CopilotRejected, v as buildCopilotSystemPrompt, y as runDataCopilot, g as validateCopilotProposal };
package/dist/cost.js ADDED
@@ -0,0 +1,242 @@
1
+ import { AI_BUDGET_RESERVATIONS_TABLE as e, AI_BUDGET_TABLE as t, AiBudgetExceeded as n, AiBudgetInvalidInput as r, AiBudgetReservationConflict as i, aiBudgetReservationsTable as a, aiBudgetTable as o, releaseAiBudget as s, requireAiBudget as c } from "./budget.js";
2
+ import { Data as l, Effect as u } from "effect";
3
+ import { and as d, eq as f, generateId as p, gte as m, id as h, integer as g, queryFor as _, registerRetention as v, retentionTtlMsFromEnv as y, table as b, text as x, timestamp as S } from "@voltro/database";
4
+ //#region src/cost.ts
5
+ var C = {
6
+ "claude-opus-4-8": {
7
+ inputPer1M: 15,
8
+ outputPer1M: 75
9
+ },
10
+ "claude-opus-4-6": {
11
+ inputPer1M: 15,
12
+ outputPer1M: 75
13
+ },
14
+ "claude-sonnet-4-6": {
15
+ inputPer1M: 3,
16
+ outputPer1M: 15
17
+ },
18
+ "claude-haiku-4-5": {
19
+ inputPer1M: 1,
20
+ outputPer1M: 5
21
+ },
22
+ "claude-haiku-4-5-20251001": {
23
+ inputPer1M: 1,
24
+ outputPer1M: 5
25
+ },
26
+ "gpt-5.5": {
27
+ inputPer1M: 5,
28
+ outputPer1M: 30
29
+ },
30
+ "gpt-4o": {
31
+ inputPer1M: 2.5,
32
+ outputPer1M: 10
33
+ },
34
+ "gpt-4o-mini": {
35
+ inputPer1M: .15,
36
+ outputPer1M: .6
37
+ },
38
+ "gemini-2.5-pro": {
39
+ inputPer1M: 1.25,
40
+ outputPer1M: 10
41
+ },
42
+ "gemini-2.5-flash": {
43
+ inputPer1M: .3,
44
+ outputPer1M: 2.5
45
+ },
46
+ "gemini-2.0-flash": {
47
+ inputPer1M: .1,
48
+ outputPer1M: .4
49
+ }
50
+ }, w = {}, T = (e) => {
51
+ let t = w;
52
+ return w = { ...e }, t;
53
+ }, E = () => ({
54
+ ...C,
55
+ ...w
56
+ }), D = (e) => {
57
+ let t = E();
58
+ if (t[e] !== void 0) return t[e];
59
+ let n = e.indexOf("/");
60
+ if (n > 0) {
61
+ let r = e.slice(n + 1);
62
+ if (t[r] !== void 0) return t[r];
63
+ }
64
+ }, O = (e) => typeof e.unit == "string", k = (e) => O(e) ? {
65
+ unit: e.unit,
66
+ input: e.input ?? 0,
67
+ output: e.output ?? 0
68
+ } : {
69
+ unit: "tokens",
70
+ input: e.inputTokens ?? 0,
71
+ output: e.outputTokens ?? 0
72
+ }, A = (e, t) => {
73
+ let { unit: n, input: r, output: i } = k(e);
74
+ if (t.actualCostUsd !== void 0) {
75
+ if (!Number.isFinite(t.actualCostUsd) || t.actualCostUsd < 0) throw RangeError("estimateCostUsd: actualCostUsd must be a finite, non-negative USD amount.");
76
+ let e = r + i, a = e > 0 ? r / e : 0;
77
+ return {
78
+ inputTokens: r,
79
+ outputTokens: i,
80
+ inputCostUsd: t.actualCostUsd * a,
81
+ outputCostUsd: t.actualCostUsd * (1 - a),
82
+ totalCostUsd: t.actualCostUsd,
83
+ costSource: "gateway",
84
+ unit: n
85
+ };
86
+ }
87
+ if (n !== "tokens" && t.price === void 0) return {
88
+ inputTokens: r,
89
+ outputTokens: i,
90
+ inputCostUsd: 0,
91
+ outputCostUsd: 0,
92
+ totalCostUsd: 0,
93
+ costSource: "unpriced",
94
+ unit: n
95
+ };
96
+ let a = t.price ?? D(t.model) ?? {
97
+ inputPer1M: 0,
98
+ outputPer1M: 0
99
+ }, o = r / 1e6 * a.inputPer1M, s = i / 1e6 * a.outputPer1M;
100
+ return {
101
+ inputTokens: r,
102
+ outputTokens: i,
103
+ inputCostUsd: o,
104
+ outputCostUsd: s,
105
+ totalCostUsd: o + s,
106
+ costSource: "estimated",
107
+ unit: n
108
+ };
109
+ }, j = "_voltro_ai_usage", M = b(j, {
110
+ id: h({ prefix: "aiu" }),
111
+ tenantId: x().nullable(),
112
+ provider: x(),
113
+ model: x(),
114
+ operation: x(),
115
+ agent: x().nullable(),
116
+ inputTokens: g().default(0),
117
+ outputTokens: g().default(0),
118
+ costMicroUsd: g().default(0),
119
+ costSource: x().default("estimated"),
120
+ unit: x().default("tokens"),
121
+ promptId: x().nullable(),
122
+ promptDigest: x().nullable(),
123
+ promptRevision: g().nullable(),
124
+ requestId: x().maxLength(191).nullable(),
125
+ attempt: g().nullable(),
126
+ receiptKey: x().maxLength(191).backfill((e) => `aiu-pre-receipt-${String(e.id)}`),
127
+ calledAt: S().default("now")
128
+ }).index(["tenantId"]).index(["promptDigest"]).unique("byAiUsageReceipt", ["receiptKey"]), N = (e) => {
129
+ let t = typeof e == "string" ? e.trim() : void 0, n = typeof e == "number" ? e : t !== void 0 && /^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(t) ? Number(t) : NaN;
130
+ return Number.isFinite(n) && n >= 0 ? n : void 0;
131
+ }, P = (e) => {
132
+ let t = e?.gateway, n = N(t?.cost);
133
+ if (n === void 0) return;
134
+ let r = (Array.isArray(t?.routing?.modelAttempts) ? t.routing.modelAttempts : []).findLast((e) => e?.success === !0);
135
+ if ((Array.isArray(r?.providerAttempts) ? r.providerAttempts : []).findLast((e) => e?.success === !0)?.credentialType !== "byok") return n;
136
+ let i = N(t?.marketCost);
137
+ return i === void 0 ? void 0 : n + i;
138
+ }, F = class extends l.TaggedError("AiUsageReceiptConflict") {
139
+ get message() {
140
+ let e = Object.keys(this.recorded).filter((e) => this.recorded[e] !== this.attempted[e]).map((e) => `${e} ${String(this.recorded[e])} → ${String(this.attempted[e])}`);
141
+ return `AI usage receipt ${this.receiptKey} was recorded again with different values (${e.join(", ")}). A replay of the same paid call has the same values; the row keeps what was recorded first.`;
142
+ }
143
+ }, I = class extends l.TaggedError("AiUsageLedgerError") {
144
+ get message() {
145
+ return `AI usage receipt ${this.receiptKey} could not be written: ${this.cause instanceof Error ? this.cause.message : String(this.cause)}`;
146
+ }
147
+ }, L = (e) => ({
148
+ tenantId: e.tenantId ?? null,
149
+ provider: String(e.provider),
150
+ model: String(e.model),
151
+ operation: String(e.operation),
152
+ inputTokens: Number(e.inputTokens),
153
+ outputTokens: Number(e.outputTokens),
154
+ costMicroUsd: Number(e.costMicroUsd)
155
+ }), R = (e, t) => (v({
156
+ source: "plugin",
157
+ table: j,
158
+ timeColumn: "calledAt",
159
+ ttlMs: y(process.env.VOLTRO_AI_USAGE_TTL_HOURS, 8760)
160
+ }), u.gen(function* () {
161
+ let n = A(t.usage, {
162
+ model: t.model,
163
+ ...t.price === void 0 ? {} : { price: t.price },
164
+ ...t.actualCostUsd === void 0 ? {} : { actualCostUsd: t.actualCostUsd }
165
+ }), r = t.receiptKey ?? (t.requestId === void 0 ? p({
166
+ kind: "typeid",
167
+ prefix: "aiu"
168
+ }, "_voltro_ai_usage") : `${t.requestId}#${t.attempt ?? 0}`), i = {
169
+ tenantId: t.tenantId ?? null,
170
+ provider: t.provider,
171
+ model: t.model,
172
+ operation: t.operation,
173
+ agent: t.agent ?? null,
174
+ inputTokens: n.inputTokens,
175
+ outputTokens: n.outputTokens,
176
+ costMicroUsd: Math.round(n.totalCostUsd * 1e6),
177
+ costSource: n.costSource,
178
+ unit: n.unit,
179
+ promptId: t.prompt?.promptId ?? null,
180
+ promptDigest: t.prompt?.digest ?? null,
181
+ promptRevision: t.prompt?.revision ?? null,
182
+ requestId: t.requestId ?? null,
183
+ attempt: t.attempt ?? null,
184
+ receiptKey: r,
185
+ calledAt: t.calledAt ?? /* @__PURE__ */ new Date()
186
+ }, a = yield* u.tryPromise({
187
+ try: () => e.insertIgnoreWithOutcome(j, i, { conflictColumns: ["receiptKey"] }),
188
+ catch: (e) => new I({
189
+ receiptKey: String(r),
190
+ cause: e
191
+ })
192
+ });
193
+ if (a.outcome === "inserted") return {
194
+ ...n,
195
+ receipt: "recorded"
196
+ };
197
+ let o = L(a.row), s = L(i);
198
+ return Object.keys(o).some((e) => o[e] !== s[e]) ? yield* u.fail(new F({
199
+ receiptKey: String(r),
200
+ recorded: o,
201
+ attempted: s
202
+ })) : {
203
+ ...z(a.row, n),
204
+ receipt: "replayed"
205
+ };
206
+ })), z = (e, t) => {
207
+ let n = Number(e.costMicroUsd) / 1e6;
208
+ return {
209
+ ...t,
210
+ inputTokens: Number(e.inputTokens),
211
+ outputTokens: Number(e.outputTokens),
212
+ totalCostUsd: n,
213
+ costSource: e.costSource ?? t.costSource
214
+ };
215
+ }, B = (e, t) => R(e, t).pipe(u.asVoid, u.catchCause((e) => u.logError("AI usage NOT booked as recorded — the call itself succeeded and was returned", e).pipe(u.annotateLogs({
216
+ scope: "voltro:ai:usage",
217
+ provider: t.provider,
218
+ model: t.model,
219
+ operation: t.operation,
220
+ ...O(t.usage) ? {
221
+ unit: t.usage.unit,
222
+ input: t.usage.input ?? null,
223
+ output: t.usage.output ?? null
224
+ } : {
225
+ inputTokens: t.usage.inputTokens ?? null,
226
+ outputTokens: t.usage.outputTokens ?? null
227
+ },
228
+ ...t.requestId === void 0 ? {} : {
229
+ requestId: t.requestId,
230
+ attempt: t.attempt ?? 0
231
+ },
232
+ ...t.actualCostUsd === void 0 ? {} : { actualCostUsd: t.actualCostUsd }
233
+ })))), V = (e, t = {}) => u.promise(async () => {
234
+ let n = [
235
+ ...t.tenantId === void 0 ? [] : [f("tenantId", t.tenantId)],
236
+ ...t.since === void 0 ? [] : [m("calledAt", t.since)],
237
+ ...t.promptDigest === void 0 ? [] : [f("promptDigest", t.promptDigest)]
238
+ ], r = _(M), i = n.length === 0 ? r : r.where(n.length === 1 ? n[0] : d(...n));
239
+ return (await e.query(i.descriptor)).reduce((e, t) => e + (Number(t.costMicroUsd) || 0), 0) / 1e6;
240
+ });
241
+ //#endregion
242
+ export { e as AI_BUDGET_RESERVATIONS_TABLE, t as AI_BUDGET_TABLE, j as AI_USAGE_TABLE, n as AiBudgetExceeded, r as AiBudgetInvalidInput, i as AiBudgetReservationConflict, I as AiUsageLedgerError, F as AiUsageReceiptConflict, C as MODEL_PRICING_DEFAULTS, a as aiBudgetReservationsTable, o as aiBudgetTable, V as aiSpendUsd, M as aiUsageTable, A as estimateCostUsd, P as gatewayCostUsd, O as isMeteredUsage, E as modelPricing, k as normaliseUsage, D as priceForModel, R as recordAiUsage, B as recordAiUsageBestEffort, s as releaseAiBudget, c as requireAiBudget, T as setModelPricing };
package/dist/embed.js ADDED
@@ -0,0 +1,41 @@
1
+ import { aiGenerationError as e } from "./api.js";
2
+ import { embeddingProviderFromEnv as t, resolveEmbeddingModel as n } from "./embeddingProvider.js";
3
+ import { cosineSimilarity as r, embed as i, embedMany as a } from "ai";
4
+ import { Effect as o } from "effect";
5
+ //#region src/embed.ts
6
+ var s = (r, a) => o.tryPromise({
7
+ try: async (e) => {
8
+ let o = a ?? t();
9
+ l(o.expectedDimensions);
10
+ let s = await n(o), c = await i({
11
+ model: s,
12
+ value: r,
13
+ abortSignal: e,
14
+ ...o.providerOptions === void 0 ? {} : { providerOptions: o.providerOptions }
15
+ });
16
+ return u(c.embedding, o.expectedDimensions, 0), c.embedding;
17
+ },
18
+ catch: (t) => e(t)
19
+ }), c = (r, i) => o.tryPromise({
20
+ try: async (e) => {
21
+ let o = i ?? t();
22
+ l(o.expectedDimensions);
23
+ let s = await n(o), c = await a({
24
+ model: s,
25
+ values: [...r],
26
+ abortSignal: e,
27
+ ...o.providerOptions === void 0 ? {} : { providerOptions: o.providerOptions }
28
+ });
29
+ if (c.embeddings.length !== r.length) throw Error(`embedMany: expected ${r.length} vectors, received ${c.embeddings.length}`);
30
+ return c.embeddings.forEach((e, t) => u(e, o.expectedDimensions, t)), c.embeddings;
31
+ },
32
+ catch: (t) => e(t)
33
+ }), l = (e) => {
34
+ if (e !== void 0 && (!Number.isSafeInteger(e) || e <= 0)) throw Error("embedding expectedDimensions must be a positive safe integer");
35
+ }, u = (e, t, n) => {
36
+ if (!Array.isArray(e)) throw Error(`embedding ${n}: expected a vector array`);
37
+ if (t !== void 0 && e.length !== t) throw Error(`embedding ${n}: expected ${t} dimensions, received ${e.length}`);
38
+ if (e.length === 0 || e.some((e) => !Number.isFinite(e))) throw Error(`embedding ${n}: expected a non-empty vector of finite numbers`);
39
+ }, d = (e, t) => r([...e], [...t]);
40
+ //#endregion
41
+ export { d as cosineSimilarity, s as embed, c as embedMany };
@@ -0,0 +1,51 @@
1
+ import { createGateway as e, gateway as t } from "@ai-sdk/gateway";
2
+ //#region src/embeddingProvider.ts
3
+ var n = (e, t) => ({
4
+ specificationVersion: "v4",
5
+ provider: "mock",
6
+ modelId: e,
7
+ maxEmbeddingsPerCall: 2048,
8
+ supportsParallelCalls: !0,
9
+ doEmbed: async ({ values: e }) => ({
10
+ embeddings: e.map((e) => r(e, t)),
11
+ usage: { tokens: e.reduce((e, t) => e + t.length, 0) },
12
+ warnings: []
13
+ })
14
+ }), r = (e, t) => {
15
+ let n = Array(t);
16
+ for (let r = 0; r < t; r++) {
17
+ let t = 2166136261 ^ r * 16777619;
18
+ for (let n = 0; n < e.length; n++) t ^= e.charCodeAt(n), t = Math.imul(t, 16777619);
19
+ n[r] = (t >>> 0) / 4294967295 * 2 - 1;
20
+ }
21
+ return n;
22
+ }, i = async (r) => {
23
+ switch (r.name) {
24
+ case "mock": return n(r.model, r.mockDim ?? 8);
25
+ case "openai": return (await a("@ai-sdk/openai", "openai")).openai.textEmbeddingModel(r.model);
26
+ case "voyage": return (await a("voyage-ai-provider", "voyage")).voyage.textEmbeddingModel(r.model);
27
+ case "cohere": return (await a("@ai-sdk/cohere", "cohere")).cohere.textEmbeddingModel(r.model);
28
+ case "gateway": return (r.apiKey !== void 0 || r.baseURL !== void 0 ? e({
29
+ ...r.apiKey === void 0 ? {} : { apiKey: r.apiKey },
30
+ ...r.baseURL === void 0 ? {} : { baseURL: r.baseURL }
31
+ }) : t).embeddingModel(r.model);
32
+ }
33
+ }, a = async (e, t) => {
34
+ try {
35
+ return await import(
36
+ /* @vite-ignore */
37
+ e
38
+ );
39
+ } catch {
40
+ throw Error(`embedding provider '${t}' needs the '${e}' package — install it (pnpm add ${e}) or use AI_EMBED_PROVIDER=mock for tests/demos.`);
41
+ }
42
+ }, o = (e = process.env) => {
43
+ let t = (e.AI_EMBED_PROVIDER ?? "mock").toLowerCase(), n = t === "openai" ? "text-embedding-3-small" : t === "voyage" ? "voyage-3" : t === "cohere" ? "embed-english-v3.0" : t === "gateway" ? "openai/text-embedding-3-small" : "mock", r = e.AI_EMBED_MODEL ?? n, i = e.AI_EMBED_MOCK_DIM === void 0 ? void 0 : Number(e.AI_EMBED_MOCK_DIM);
44
+ return {
45
+ name: t,
46
+ model: r,
47
+ ...i !== void 0 && Number.isFinite(i) ? { mockDim: i } : {}
48
+ };
49
+ };
50
+ //#endregion
51
+ export { o as embeddingProviderFromEnv, r as hashToVector, i as resolveEmbeddingModel };
package/dist/eval.js ADDED
@@ -0,0 +1,130 @@
1
+ import { providerFromEnv as e } from "./provider.js";
2
+ import { generateObject as t } from "./api.js";
3
+ import { Effect as n, Schema as r } from "effect";
4
+ //#region src/eval.ts
5
+ var i = (e) => {
6
+ if (e.name.trim() === "") throw Error("defineEval: name must be non-empty");
7
+ if (e.cases.length === 0) throw Error(`defineEval "${e.name}": at least one case is required`);
8
+ let t = /* @__PURE__ */ new Set();
9
+ for (let n of e.cases) {
10
+ if (n.id.trim() === "") throw Error(`defineEval "${e.name}": every case needs a non-empty id`);
11
+ if (t.has(n.id)) throw Error(`defineEval "${e.name}": duplicate case id "${n.id}"`);
12
+ t.add(n.id);
13
+ }
14
+ let n = e.judge?.threshold;
15
+ if (n !== void 0 && (n < 0 || n > 1)) throw Error(`defineEval "${e.name}": judge.threshold must be within 0..1 (got ${n})`);
16
+ return {
17
+ ...e,
18
+ __eval: !0
19
+ };
20
+ }, a = (e) => typeof e == "object" && !!e && "__eval" in e && e.__eval === !0, o = (e, t) => {
21
+ let n = [], r = e.text;
22
+ for (let i of t) switch (i.kind) {
23
+ case "contains": {
24
+ let e = i.caseInsensitive ? r.toLowerCase() : r, t = i.caseInsensitive ? i.value.toLowerCase() : i.value;
25
+ e.includes(t) || n.push({
26
+ assertion: i,
27
+ detail: `output does not contain "${i.value}"`
28
+ });
29
+ break;
30
+ }
31
+ case "notContains": {
32
+ let e = i.caseInsensitive ? r.toLowerCase() : r, t = i.caseInsensitive ? i.value.toLowerCase() : i.value;
33
+ e.includes(t) && n.push({
34
+ assertion: i,
35
+ detail: `output unexpectedly contains "${i.value}"`
36
+ });
37
+ break;
38
+ }
39
+ case "matches": {
40
+ let e;
41
+ try {
42
+ e = new RegExp(i.pattern, i.flags);
43
+ } catch (e) {
44
+ n.push({
45
+ assertion: i,
46
+ detail: `invalid pattern /${i.pattern}/: ${e.message}`
47
+ });
48
+ break;
49
+ }
50
+ e.test(r) || n.push({
51
+ assertion: i,
52
+ detail: `output does not match /${i.pattern}/`
53
+ });
54
+ break;
55
+ }
56
+ case "equals":
57
+ (i.trim ? r.trim() : r) !== (i.trim ? i.value.trim() : i.value) && n.push({
58
+ assertion: i,
59
+ detail: "output is not equal to the expected value"
60
+ });
61
+ break;
62
+ case "maxLatencyMs":
63
+ e.latencyMs > i.value && n.push({
64
+ assertion: i,
65
+ detail: `latency ${e.latencyMs}ms exceeds ${i.value}ms`
66
+ });
67
+ break;
68
+ case "nonEmpty": r.trim() === "" && n.push({
69
+ assertion: i,
70
+ detail: "output is empty"
71
+ });
72
+ }
73
+ return n;
74
+ }, s = .7, c = (e, t, n, r, i) => {
75
+ let a = o(t, [...n, ...e.assert ?? []]), s = r?.threshold ?? .7, c = i === void 0 || i.score >= s;
76
+ return {
77
+ id: e.id,
78
+ pass: a.length === 0 && c,
79
+ assertionFailures: a,
80
+ ...i === void 0 ? {} : { judge: i },
81
+ output: t
82
+ };
83
+ }, l = async (e, t) => {
84
+ let n = [];
85
+ for (let r of e.cases) {
86
+ let i = await t.replay({
87
+ case: r,
88
+ def: e
89
+ }), a = e.judge !== void 0 && t.judge !== void 0 ? await t.judge({
90
+ case: r,
91
+ output: i,
92
+ config: e.judge
93
+ }) : void 0;
94
+ n.push(c(r, i, e.assert ?? [], e.judge, a));
95
+ }
96
+ let r = n.filter((e) => !e.pass).length;
97
+ return {
98
+ name: e.name,
99
+ ok: r === 0,
100
+ passed: n.length - r,
101
+ failed: r,
102
+ cases: n,
103
+ ...t.branchId === void 0 ? {} : { branch: { id: t.branchId } }
104
+ };
105
+ }, u = r.Struct({
106
+ pass: r.Boolean,
107
+ score: r.Number,
108
+ reason: r.String
109
+ }), d = (e, t, n) => [
110
+ "You are grading an AI assistant response against a rubric. Respond with a JSON",
111
+ "object { pass: boolean, score: number (0..1), reason: string }. Score 1 is a",
112
+ "perfect response, 0 is unusable. Be strict: a plausible-sounding but wrong or",
113
+ "incomplete answer scores low.",
114
+ "",
115
+ `RUBRIC:\n${n.rubric}`,
116
+ ...e.golden === void 0 ? [] : ["", `REFERENCE (the previously-accepted answer):\n${e.golden}`],
117
+ ...e.input.prompt === "" ? [] : ["", `USER PROMPT:\n${e.input.prompt}`],
118
+ "",
119
+ `CANDIDATE RESPONSE (to grade):\n${t.text}`
120
+ ].join("\n"), f = async ({ case: r, output: i, config: a }) => await n.runPromise(t({
121
+ schema: u,
122
+ prompt: d(r, i, a),
123
+ provider: e()
124
+ }).pipe(n.map((e) => e.object), n.catch((e) => n.succeed({
125
+ pass: !1,
126
+ score: 0,
127
+ reason: `judge failed: ${e.message}`
128
+ }))));
129
+ //#endregion
130
+ export { s as DEFAULT_JUDGE_THRESHOLD, i as defineEval, o as evaluateAssertions, a as isEvalDefinition, f as llmJudge, l as runEval, c as scoreCase };
package/dist/events.d.ts CHANGED
@@ -17,6 +17,8 @@ export declare const AgentEvent: Schema.Union<readonly [Schema.Struct<{
17
17
  readonly id: Schema.String;
18
18
  readonly name: Schema.String;
19
19
  readonly input: Schema.Unknown;
20
+ /** Present when the caller must execute this pending call. */
21
+ readonly execution: Schema.optional<Schema.Literal<"client">>;
20
22
  }>, Schema.Struct<{
21
23
  readonly _tag: Schema.Literal<"toolResult">;
22
24
  readonly id: Schema.String;
@@ -169,6 +171,8 @@ export declare const SeqEvent: Schema.Struct<{
169
171
  readonly id: Schema.String;
170
172
  readonly name: Schema.String;
171
173
  readonly input: Schema.Unknown;
174
+ /** Present when the caller must execute this pending call. */
175
+ readonly execution: Schema.optional<Schema.Literal<"client">>;
172
176
  }>, Schema.Struct<{
173
177
  readonly _tag: Schema.Literal<"toolResult">;
174
178
  readonly id: Schema.String;