@exulu/backend 3.5.1 → 3.7.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/dist/{chunk-7AZH4ETH.js → chunk-HA2ZSGOW.js} +2 -1
- package/dist/{convert-exulu-tools-to-ai-sdk-tools-TZ2UKWR4.js → convert-exulu-tools-to-ai-sdk-tools-F3CJU6JN.js} +1 -1
- package/dist/index.cjs +274 -47
- package/dist/index.d.cts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +259 -48
- package/ee/python/requirements.txt +12 -3
- package/package.json +1 -1
|
@@ -1713,7 +1713,7 @@ var ExuluTool = class _ExuluTool {
|
|
|
1713
1713
|
if (!agent) {
|
|
1714
1714
|
throw new Error("Agent not found.");
|
|
1715
1715
|
}
|
|
1716
|
-
const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-
|
|
1716
|
+
const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-F3CJU6JN.js");
|
|
1717
1717
|
const tools = await convertExuluToolsToAiSdkTools2(
|
|
1718
1718
|
[this],
|
|
1719
1719
|
[],
|
|
@@ -8183,5 +8183,6 @@ export {
|
|
|
8183
8183
|
ExuluTool,
|
|
8184
8184
|
resolveReranker,
|
|
8185
8185
|
withRetry,
|
|
8186
|
+
microCallProviderOptions,
|
|
8186
8187
|
createAgenticRetrievalTool
|
|
8187
8188
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -8892,10 +8892,12 @@ __export(index_exports, {
|
|
|
8892
8892
|
ExuluDocumentProcessor: () => ExuluDocumentProcessor,
|
|
8893
8893
|
ExuluEval: () => ExuluEval,
|
|
8894
8894
|
ExuluJobs: () => ExuluJobs,
|
|
8895
|
+
ExuluModels: () => ExuluModels,
|
|
8895
8896
|
ExuluOtel: () => ExuluOtel,
|
|
8896
8897
|
ExuluPython: () => ExuluPython,
|
|
8897
8898
|
ExuluQueues: () => queues,
|
|
8898
8899
|
ExuluReadApi: () => ExuluReadApi,
|
|
8900
|
+
ExuluRecall: () => ExuluRecall,
|
|
8899
8901
|
ExuluReranker: () => ExuluReranker,
|
|
8900
8902
|
ExuluTool: () => ExuluTool,
|
|
8901
8903
|
ExuluVariables: () => ExuluVariables,
|
|
@@ -11452,6 +11454,52 @@ var usersSchema = {
|
|
|
11452
11454
|
// triggered by the key with project_id_ for LiteLLM cost attribution.
|
|
11453
11455
|
name: "project",
|
|
11454
11456
|
type: "uuid"
|
|
11457
|
+
},
|
|
11458
|
+
// ── Lead capture (see lead-capture spec §5) ─────────────────────────────
|
|
11459
|
+
// Consent must be *provable* under Art. 7(1) GDPR: who agreed, when, and
|
|
11460
|
+
// to which wording. Hence a timestamp per consent and a version string
|
|
11461
|
+
// pointing at the exact text that was shown.
|
|
11462
|
+
{
|
|
11463
|
+
// Current state of the optional marketing consent.
|
|
11464
|
+
name: "marketing_consent",
|
|
11465
|
+
type: "boolean",
|
|
11466
|
+
default: false
|
|
11467
|
+
},
|
|
11468
|
+
{
|
|
11469
|
+
// When marketing consent was GIVEN.
|
|
11470
|
+
name: "marketing_consent_at",
|
|
11471
|
+
type: "date"
|
|
11472
|
+
},
|
|
11473
|
+
{
|
|
11474
|
+
// When marketing consent was WITHDRAWN. Deliberately a separate column:
|
|
11475
|
+
// overwriting marketing_consent_at on withdrawal would destroy the grant
|
|
11476
|
+
// time, and with it the ability to prove the order of events if someone
|
|
11477
|
+
// later claims they were mailed after withdrawing.
|
|
11478
|
+
name: "marketing_consent_withdrawn_at",
|
|
11479
|
+
type: "date"
|
|
11480
|
+
},
|
|
11481
|
+
{
|
|
11482
|
+
// When the mandatory storage/processing consent was given.
|
|
11483
|
+
name: "processing_consent_at",
|
|
11484
|
+
type: "date"
|
|
11485
|
+
},
|
|
11486
|
+
{
|
|
11487
|
+
// When the user asked for erasure (Art. 17). The row survives so the
|
|
11488
|
+
// one-month deadline stays queryable even if the notification mail fails.
|
|
11489
|
+
name: "erasure_requested_at",
|
|
11490
|
+
type: "date"
|
|
11491
|
+
},
|
|
11492
|
+
{
|
|
11493
|
+
// Where the lead came from, e.g. "eu-ai-act-bot".
|
|
11494
|
+
name: "signup_source",
|
|
11495
|
+
type: "text"
|
|
11496
|
+
},
|
|
11497
|
+
{
|
|
11498
|
+
// Version of the consent wording that was actually displayed, e.g.
|
|
11499
|
+
// "2026-08-18". The wording itself lives in the frontend repo under
|
|
11500
|
+
// version control; this column points at it.
|
|
11501
|
+
name: "consent_version",
|
|
11502
|
+
type: "text"
|
|
11455
11503
|
}
|
|
11456
11504
|
]
|
|
11457
11505
|
};
|
|
@@ -11760,6 +11808,32 @@ var addCoreFields = (schema) => {
|
|
|
11760
11808
|
}
|
|
11761
11809
|
return schema;
|
|
11762
11810
|
};
|
|
11811
|
+
var otpSendAttemptsSchema = {
|
|
11812
|
+
type: "otp_send_attempts",
|
|
11813
|
+
name: {
|
|
11814
|
+
plural: "otp_send_attempts",
|
|
11815
|
+
singular: "otp_send_attempt"
|
|
11816
|
+
},
|
|
11817
|
+
fields: [
|
|
11818
|
+
{
|
|
11819
|
+
name: "key",
|
|
11820
|
+
type: "text",
|
|
11821
|
+
index: true,
|
|
11822
|
+
unique: true,
|
|
11823
|
+
required: true
|
|
11824
|
+
},
|
|
11825
|
+
{
|
|
11826
|
+
name: "count",
|
|
11827
|
+
type: "number",
|
|
11828
|
+
required: true
|
|
11829
|
+
},
|
|
11830
|
+
{
|
|
11831
|
+
name: "window_start",
|
|
11832
|
+
type: "date",
|
|
11833
|
+
required: true
|
|
11834
|
+
}
|
|
11835
|
+
]
|
|
11836
|
+
};
|
|
11763
11837
|
var coreSchemas = {
|
|
11764
11838
|
get: () => {
|
|
11765
11839
|
const license = checkLicense();
|
|
@@ -11780,7 +11854,8 @@ var coreSchemas = {
|
|
|
11780
11854
|
contextPresetsSchema: () => addCoreFields(contextPresetsSchema),
|
|
11781
11855
|
sharedArtifactsSchema: () => addCoreFields(sharedArtifactsSchema),
|
|
11782
11856
|
transcriptionJobsSchema: () => addCoreFields(transcriptionJobsSchema),
|
|
11783
|
-
imageGenerationsSchema: () => addCoreFields(imageGenerationsSchema)
|
|
11857
|
+
imageGenerationsSchema: () => addCoreFields(imageGenerationsSchema),
|
|
11858
|
+
otpSendAttemptsSchema: () => addCoreFields(otpSendAttemptsSchema)
|
|
11784
11859
|
};
|
|
11785
11860
|
if (license["agent-feedback"]) {
|
|
11786
11861
|
schemas.feedbackSchema = () => addCoreFields(feedbackSchema);
|
|
@@ -13641,7 +13716,7 @@ var ExuluContext2 = class {
|
|
|
13641
13716
|
}
|
|
13642
13717
|
console.log("[EXULU] context configuration", this.configuration);
|
|
13643
13718
|
let jobs = [];
|
|
13644
|
-
let
|
|
13719
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
13645
13720
|
if (this.processor) {
|
|
13646
13721
|
const processor = this.processor;
|
|
13647
13722
|
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -13663,12 +13738,12 @@ var ExuluContext2 = class {
|
|
|
13663
13738
|
...processorResult
|
|
13664
13739
|
});
|
|
13665
13740
|
if (processor.config?.generateEmbeddings) {
|
|
13666
|
-
|
|
13741
|
+
shouldGenerateEmbeddings2 = true;
|
|
13667
13742
|
}
|
|
13668
13743
|
}
|
|
13669
13744
|
}
|
|
13670
13745
|
}
|
|
13671
|
-
if (
|
|
13746
|
+
if (shouldGenerateEmbeddings2) {
|
|
13672
13747
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
13673
13748
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
13674
13749
|
item: {
|
|
@@ -13712,7 +13787,7 @@ var ExuluContext2 = class {
|
|
|
13712
13787
|
}).returning("id");
|
|
13713
13788
|
await mutation;
|
|
13714
13789
|
let jobs = [];
|
|
13715
|
-
let
|
|
13790
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
13716
13791
|
if (this.processor) {
|
|
13717
13792
|
const processor = this.processor;
|
|
13718
13793
|
if (processor && runProcessorOverwrite !== false && (runProcessorOverwrite || processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -13734,12 +13809,12 @@ var ExuluContext2 = class {
|
|
|
13734
13809
|
...processorResult
|
|
13735
13810
|
});
|
|
13736
13811
|
if (processor.config?.generateEmbeddings) {
|
|
13737
|
-
|
|
13812
|
+
shouldGenerateEmbeddings2 = true;
|
|
13738
13813
|
}
|
|
13739
13814
|
}
|
|
13740
13815
|
}
|
|
13741
13816
|
}
|
|
13742
|
-
if (
|
|
13817
|
+
if (shouldGenerateEmbeddings2) {
|
|
13743
13818
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
13744
13819
|
item: record,
|
|
13745
13820
|
// important we need to full record here with all fields for the embedder
|
|
@@ -15595,6 +15670,18 @@ var applyAgentGuestFieldTransforms = async (input) => {
|
|
|
15595
15670
|
return input;
|
|
15596
15671
|
};
|
|
15597
15672
|
|
|
15673
|
+
// src/graphql/mutations/should-generate-embeddings.ts
|
|
15674
|
+
init_cjs_shims();
|
|
15675
|
+
var shouldGenerateEmbeddings = ({
|
|
15676
|
+
calculateVectors,
|
|
15677
|
+
operation,
|
|
15678
|
+
override
|
|
15679
|
+
}) => {
|
|
15680
|
+
if (override !== void 0) return override;
|
|
15681
|
+
if (calculateVectors === "always") return true;
|
|
15682
|
+
return operation === "create" ? calculateVectors === "onInsert" : calculateVectors === "onUpdate";
|
|
15683
|
+
};
|
|
15684
|
+
|
|
15598
15685
|
// src/graphql/mutations/index.ts
|
|
15599
15686
|
var VALID_RIGHTS_MODES = ["private", "users", "roles", "teams", "public"];
|
|
15600
15687
|
var postprocessDeletion = async ({
|
|
@@ -15663,7 +15750,9 @@ var postprocessUpdate = async ({
|
|
|
15663
15750
|
result,
|
|
15664
15751
|
user,
|
|
15665
15752
|
role,
|
|
15666
|
-
config
|
|
15753
|
+
config,
|
|
15754
|
+
operation,
|
|
15755
|
+
generateEmbeddings
|
|
15667
15756
|
}) => {
|
|
15668
15757
|
if (!result) {
|
|
15669
15758
|
return result;
|
|
@@ -15690,7 +15779,11 @@ var postprocessUpdate = async ({
|
|
|
15690
15779
|
if (!context.embedder) {
|
|
15691
15780
|
return result;
|
|
15692
15781
|
}
|
|
15693
|
-
if (
|
|
15782
|
+
if (shouldGenerateEmbeddings({
|
|
15783
|
+
calculateVectors: context.configuration.calculateVectors,
|
|
15784
|
+
operation,
|
|
15785
|
+
override: generateEmbeddings
|
|
15786
|
+
})) {
|
|
15694
15787
|
const { db: db2 } = await postgresClient();
|
|
15695
15788
|
console.log("[EXULU] Deleting chunks for item", result.id);
|
|
15696
15789
|
const exists = await context.chunksTableExists();
|
|
@@ -15940,7 +16033,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
15940
16033
|
result: results[0],
|
|
15941
16034
|
user: context.user.id,
|
|
15942
16035
|
role: context.user.role?.id,
|
|
15943
|
-
config
|
|
16036
|
+
config,
|
|
16037
|
+
operation: "create",
|
|
16038
|
+
generateEmbeddings: args.generateEmbeddings
|
|
15944
16039
|
});
|
|
15945
16040
|
return {
|
|
15946
16041
|
// Filter result to only include requested fields
|
|
@@ -16014,7 +16109,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16014
16109
|
result,
|
|
16015
16110
|
user: context.user.id,
|
|
16016
16111
|
role: context.user.role?.id,
|
|
16017
|
-
config
|
|
16112
|
+
config,
|
|
16113
|
+
operation: "update",
|
|
16114
|
+
generateEmbeddings: args.generateEmbeddings
|
|
16018
16115
|
});
|
|
16019
16116
|
return {
|
|
16020
16117
|
item: finalizeRequestedFields({
|
|
@@ -16078,7 +16175,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16078
16175
|
result,
|
|
16079
16176
|
user: context.user.id,
|
|
16080
16177
|
role: context.user.role?.id,
|
|
16081
|
-
config
|
|
16178
|
+
config,
|
|
16179
|
+
operation: "update",
|
|
16180
|
+
generateEmbeddings: args.generateEmbeddings
|
|
16082
16181
|
});
|
|
16083
16182
|
return {
|
|
16084
16183
|
item: finalizeRequestedFields({
|
|
@@ -16348,6 +16447,7 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16348
16447
|
};
|
|
16349
16448
|
}
|
|
16350
16449
|
query = applyFilters(query, args.where, table);
|
|
16450
|
+
query = applyAccessControl(table, query, context.user);
|
|
16351
16451
|
if (args.limit) {
|
|
16352
16452
|
query = query.limit(args.limit);
|
|
16353
16453
|
}
|
|
@@ -19788,6 +19888,33 @@ var renderTranscript = (segments, speakers) => {
|
|
|
19788
19888
|
return blocks.map((b) => `${b.speaker}: ${b.text}`).join("\n");
|
|
19789
19889
|
};
|
|
19790
19890
|
|
|
19891
|
+
// src/exulu/transcription/build-transcript-item.ts
|
|
19892
|
+
init_cjs_shims();
|
|
19893
|
+
var buildTranscriptItemInput = ({
|
|
19894
|
+
row,
|
|
19895
|
+
title,
|
|
19896
|
+
speakers,
|
|
19897
|
+
transcriptText,
|
|
19898
|
+
rightsMode,
|
|
19899
|
+
isReSave
|
|
19900
|
+
}) => ({
|
|
19901
|
+
// Carrying the id on re-save makes context.createItem upsert in place.
|
|
19902
|
+
...isReSave && row.saved_item_id ? { id: row.saved_item_id } : {},
|
|
19903
|
+
name: title ?? row.title ?? "Transcript",
|
|
19904
|
+
transcript_text: transcriptText,
|
|
19905
|
+
audio_s3key: row.audio_s3key,
|
|
19906
|
+
language: row.language ?? void 0,
|
|
19907
|
+
duration_seconds: row.duration_seconds ?? void 0,
|
|
19908
|
+
speakers,
|
|
19909
|
+
raw_segments: row.raw_segments,
|
|
19910
|
+
// Recall meeting-bot post-processing results (null for Whisper jobs).
|
|
19911
|
+
post_processing: row.post_processing_outputs ?? void 0,
|
|
19912
|
+
// Handle for the meeting video; null for Whisper uploads.
|
|
19913
|
+
recall_recording_id: row.recall_recording_id ?? void 0,
|
|
19914
|
+
rights_mode: rightsMode,
|
|
19915
|
+
created_by: row.created_by
|
|
19916
|
+
});
|
|
19917
|
+
|
|
19791
19918
|
// src/exulu/transcription/service.ts
|
|
19792
19919
|
var TABLE2 = "transcription_jobs";
|
|
19793
19920
|
var log3 = (msg) => console.log(`[EXULU-TRANSCRIPTION] ${msg}`);
|
|
@@ -19980,21 +20107,14 @@ var transcriptionService = {
|
|
|
19980
20107
|
const transcriptText = renderTranscript(row.raw_segments, input.speakers);
|
|
19981
20108
|
const rightsMode = input.target_rights_mode ?? row.target_rights_mode ?? "private";
|
|
19982
20109
|
const isReSave = row.status === "saved" && !!row.saved_item_id;
|
|
19983
|
-
const itemInput = {
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
name: input.title ?? row.title ?? "Transcript",
|
|
19987
|
-
transcript_text: transcriptText,
|
|
19988
|
-
audio_s3key: row.audio_s3key,
|
|
19989
|
-
language: row.language ?? void 0,
|
|
19990
|
-
duration_seconds: row.duration_seconds ?? void 0,
|
|
20110
|
+
const itemInput = buildTranscriptItemInput({
|
|
20111
|
+
row,
|
|
20112
|
+
title: input.title,
|
|
19991
20113
|
speakers: input.speakers,
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
created_by: row.created_by
|
|
19997
|
-
};
|
|
20114
|
+
transcriptText,
|
|
20115
|
+
rightsMode,
|
|
20116
|
+
isReSave
|
|
20117
|
+
});
|
|
19998
20118
|
let item;
|
|
19999
20119
|
try {
|
|
20000
20120
|
const result = await context.createItem(
|
|
@@ -20240,25 +20360,34 @@ var recordingDurationSeconds = (rec) => {
|
|
|
20240
20360
|
}
|
|
20241
20361
|
return null;
|
|
20242
20362
|
};
|
|
20363
|
+
var RECORDING_RETENTION_HOURS = 2160;
|
|
20364
|
+
var buildCreateBotPayload = (input) => ({
|
|
20365
|
+
meeting_url: input.meeting_url,
|
|
20366
|
+
join_at: input.join_at,
|
|
20367
|
+
...input.bot_name ? { bot_name: input.bot_name } : {},
|
|
20368
|
+
recording_config: {
|
|
20369
|
+
video_mixed_mp4: {},
|
|
20370
|
+
video_mixed_layout: "speaker_view",
|
|
20371
|
+
participant_events: {},
|
|
20372
|
+
meeting_metadata: {},
|
|
20373
|
+
retention: { type: "timed", hours: RECORDING_RETENTION_HOURS }
|
|
20374
|
+
},
|
|
20375
|
+
...input.notifyChat ? {
|
|
20376
|
+
chat: {
|
|
20377
|
+
on_bot_join: {
|
|
20378
|
+
send_to: "everyone",
|
|
20379
|
+
message: input.notifyChat.message,
|
|
20380
|
+
pin: true
|
|
20381
|
+
}
|
|
20382
|
+
}
|
|
20383
|
+
} : {}
|
|
20384
|
+
});
|
|
20243
20385
|
var recallClient = {
|
|
20244
20386
|
isConfigured: () => !!recallApiKey() && !!process.env.RECALL_REGION,
|
|
20245
20387
|
/** POST /bot — schedule/launch a bot for a meeting. */
|
|
20246
20388
|
createBot: (input) => request2("/bot/", {
|
|
20247
20389
|
method: "POST",
|
|
20248
|
-
body: JSON.stringify(
|
|
20249
|
-
meeting_url: input.meeting_url,
|
|
20250
|
-
join_at: input.join_at,
|
|
20251
|
-
...input.bot_name ? { bot_name: input.bot_name } : {},
|
|
20252
|
-
...input.notifyChat ? {
|
|
20253
|
-
chat: {
|
|
20254
|
-
on_bot_join: {
|
|
20255
|
-
send_to: "everyone",
|
|
20256
|
-
message: input.notifyChat.message,
|
|
20257
|
-
pin: true
|
|
20258
|
-
}
|
|
20259
|
-
}
|
|
20260
|
-
} : {}
|
|
20261
|
-
})
|
|
20390
|
+
body: JSON.stringify(buildCreateBotPayload(input))
|
|
20262
20391
|
}),
|
|
20263
20392
|
/**
|
|
20264
20393
|
* POST /recording/{id}/create_transcript — start a post-meeting (async)
|
|
@@ -20574,6 +20703,28 @@ var recallService = {
|
|
|
20574
20703
|
}
|
|
20575
20704
|
return null;
|
|
20576
20705
|
},
|
|
20706
|
+
/**
|
|
20707
|
+
* Fresh signed URL for a recording's mixed MP4, or null when there is no
|
|
20708
|
+
* usable video (still processing, no video artifact, recording expired or
|
|
20709
|
+
* deleted).
|
|
20710
|
+
*
|
|
20711
|
+
* Never cache the result: the URL carries X-Amz-Expires=21600, so it dies
|
|
20712
|
+
* after six hours. Resolve at point of use.
|
|
20713
|
+
*/
|
|
20714
|
+
async getRecordingVideoUrl(recordingId) {
|
|
20715
|
+
try {
|
|
20716
|
+
const recording = await recallClient.retrieveRecording(recordingId);
|
|
20717
|
+
const video = recording?.media_shortcuts?.video_mixed;
|
|
20718
|
+
if (!video) return null;
|
|
20719
|
+
if (video.status?.code && video.status.code !== "done") return null;
|
|
20720
|
+
return video.data?.download_url ?? null;
|
|
20721
|
+
} catch (err) {
|
|
20722
|
+
log4(
|
|
20723
|
+
`could not resolve video url for recording ${recordingId}: ${err.message}`
|
|
20724
|
+
);
|
|
20725
|
+
return null;
|
|
20726
|
+
}
|
|
20727
|
+
},
|
|
20577
20728
|
/**
|
|
20578
20729
|
* Run the selected {prompt, agent} pairs against the transcript and store the
|
|
20579
20730
|
* results on the row. Idempotent (skips if outputs already exist). Each entry
|
|
@@ -21469,11 +21620,11 @@ function createSDL(tables, contexts, tools, config, evals) {
|
|
|
21469
21620
|
`;
|
|
21470
21621
|
}
|
|
21471
21622
|
mutationDefs += `
|
|
21472
|
-
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean): ${tableNameSingular}MutationPayload
|
|
21623
|
+
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
21473
21624
|
${tableNamePlural}CopyOneById(id: ID!): ${tableNameSingular}MutationPayload
|
|
21474
21625
|
|
|
21475
|
-
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input
|
|
21476
|
-
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input
|
|
21626
|
+
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input!, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
21627
|
+
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input!, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
21477
21628
|
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
21478
21629
|
${tableNamePlural}RemoveOne(where: JSON!): ${tableNameSingular}
|
|
21479
21630
|
`;
|
|
@@ -30179,7 +30330,11 @@ var transcriptionsContext = new ExuluContext2({
|
|
|
30179
30330
|
// Post-processing results carried from the transcription job at finalize:
|
|
30180
30331
|
// [{ prompt_id, agent_id, prompt_name, output, ran_at }]. Recall meeting
|
|
30181
30332
|
// transcripts only for now.
|
|
30182
|
-
{ name: "post_processing", type: "json", editable: false }
|
|
30333
|
+
{ name: "post_processing", type: "json", editable: false },
|
|
30334
|
+
// Link back to the Recall recording so the mixed video stays reachable
|
|
30335
|
+
// (resolve a fresh URL via ExuluRecall.getRecordingVideoUrl — it expires
|
|
30336
|
+
// after six hours). Null for Whisper uploads.
|
|
30337
|
+
{ name: "recall_recording_id", type: "text" }
|
|
30183
30338
|
],
|
|
30184
30339
|
sources: [],
|
|
30185
30340
|
active: true,
|
|
@@ -31533,6 +31688,16 @@ init_supervisor();
|
|
|
31533
31688
|
init_cjs_shims();
|
|
31534
31689
|
init_client();
|
|
31535
31690
|
init_sanitize_name();
|
|
31691
|
+
|
|
31692
|
+
// src/exulu/context-fields-for-sync.ts
|
|
31693
|
+
init_cjs_shims();
|
|
31694
|
+
var contextFieldsForSync = (context) => (context.fields ?? []).filter((field) => !!field?.name && !!field?.type).map((field) => ({
|
|
31695
|
+
...field,
|
|
31696
|
+
name: field.type === "file" ? `${field.name}_s3key` : field.name
|
|
31697
|
+
}));
|
|
31698
|
+
|
|
31699
|
+
// src/postgres/init-exulu-db.ts
|
|
31700
|
+
init_table_names();
|
|
31536
31701
|
var {
|
|
31537
31702
|
agentsSchema: agentsSchema3,
|
|
31538
31703
|
feedbackSchema: feedbackSchema3,
|
|
@@ -31560,7 +31725,8 @@ var {
|
|
|
31560
31725
|
promptFavoritesSchema: promptFavoritesSchema3,
|
|
31561
31726
|
transcriptionJobsSchema: transcriptionJobsSchema3,
|
|
31562
31727
|
imageGenerationsSchema: imageGenerationsSchema2,
|
|
31563
|
-
sharedArtifactsSchema: sharedArtifactsSchema2
|
|
31728
|
+
sharedArtifactsSchema: sharedArtifactsSchema2,
|
|
31729
|
+
otpSendAttemptsSchema: otpSendAttemptsSchema2
|
|
31564
31730
|
} = coreSchemas.get();
|
|
31565
31731
|
var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
31566
31732
|
for (const field of fields) {
|
|
@@ -31578,8 +31744,9 @@ var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
|
31578
31744
|
await knex.schema.alterTable(tableName, (table) => {
|
|
31579
31745
|
mapType(table, type, sanitizedName, defaultValue, unique);
|
|
31580
31746
|
});
|
|
31747
|
+
} else {
|
|
31748
|
+
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
31581
31749
|
}
|
|
31582
|
-
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
31583
31750
|
}
|
|
31584
31751
|
};
|
|
31585
31752
|
var migrateUserCredentialsDataColumn = async (knex) => {
|
|
@@ -31631,7 +31798,8 @@ var up = async function(knex) {
|
|
|
31631
31798
|
variablesSchema3(),
|
|
31632
31799
|
skillsSchema3(),
|
|
31633
31800
|
workflowTemplatesSchema3(),
|
|
31634
|
-
workflowTriggersSchema2()
|
|
31801
|
+
workflowTriggersSchema2(),
|
|
31802
|
+
otpSendAttemptsSchema2()
|
|
31635
31803
|
];
|
|
31636
31804
|
const createTable = async (schema) => {
|
|
31637
31805
|
if (!await knex.schema.hasTable(schema.name.plural)) {
|
|
@@ -31762,11 +31930,18 @@ var up = async function(knex) {
|
|
|
31762
31930
|
}
|
|
31763
31931
|
};
|
|
31764
31932
|
var contextDatabases = async (contexts) => {
|
|
31933
|
+
const { db: knex } = await postgresClient();
|
|
31765
31934
|
for (const context of contexts) {
|
|
31766
31935
|
const itemsTableExists = await context.tableExists();
|
|
31767
31936
|
if (!itemsTableExists) {
|
|
31768
31937
|
console.log("[EXULU] items table does not exist, creating it.");
|
|
31769
31938
|
await context.createItemsTable();
|
|
31939
|
+
} else {
|
|
31940
|
+
await addMissingFields(
|
|
31941
|
+
knex,
|
|
31942
|
+
getTableName(context.id),
|
|
31943
|
+
contextFieldsForSync(context)
|
|
31944
|
+
);
|
|
31770
31945
|
}
|
|
31771
31946
|
const chunksTableExists = await context.chunksTableExists();
|
|
31772
31947
|
if (!chunksTableExists && context.embedder) {
|
|
@@ -33647,8 +33822,58 @@ async function rerank(input) {
|
|
|
33647
33822
|
|
|
33648
33823
|
// src/index.ts
|
|
33649
33824
|
init_pipeline();
|
|
33825
|
+
|
|
33826
|
+
// src/exulu/models/public.ts
|
|
33827
|
+
init_cjs_shims();
|
|
33828
|
+
init_resolve_model();
|
|
33829
|
+
init_client();
|
|
33830
|
+
init_micro_call();
|
|
33831
|
+
var ExuluModels = {
|
|
33832
|
+
/**
|
|
33833
|
+
* A LanguageModel bound to the tenant's LiteLLM proxy.
|
|
33834
|
+
*
|
|
33835
|
+
* Pass `userId` wherever one is known — it is what carries the caller's
|
|
33836
|
+
* identity tags into LiteLLM, and therefore what makes the spend visible to
|
|
33837
|
+
* that user's team budget. Resolution still succeeds without it; the calls
|
|
33838
|
+
* are simply unattributed.
|
|
33839
|
+
*/
|
|
33840
|
+
resolve: async ({
|
|
33841
|
+
modelId,
|
|
33842
|
+
userId
|
|
33843
|
+
}) => {
|
|
33844
|
+
let user;
|
|
33845
|
+
if (userId != null) {
|
|
33846
|
+
const { db: db2 } = await postgresClient();
|
|
33847
|
+
user = await db2("users").where({ id: userId }).first();
|
|
33848
|
+
}
|
|
33849
|
+
const { languageModel } = await resolveModel({ modelId, user: user || void 0 });
|
|
33850
|
+
return languageModel;
|
|
33851
|
+
},
|
|
33852
|
+
/**
|
|
33853
|
+
* Provider options for a constrained structured-output call.
|
|
33854
|
+
*
|
|
33855
|
+
* Gemini 3+ counts thinking tokens against maxOutputTokens, so a call with a
|
|
33856
|
+
* token cap returns an empty 200 unless reasoning is disabled. Returns
|
|
33857
|
+
* undefined for non-Gemini models.
|
|
33858
|
+
*/
|
|
33859
|
+
providerOptions: (model) => microCallProviderOptions(model)
|
|
33860
|
+
};
|
|
33861
|
+
|
|
33862
|
+
// src/index.ts
|
|
33650
33863
|
init_statistics();
|
|
33651
33864
|
init_errors();
|
|
33865
|
+
|
|
33866
|
+
// src/exulu/recall/public.ts
|
|
33867
|
+
init_cjs_shims();
|
|
33868
|
+
var ExuluRecall = {
|
|
33869
|
+
/**
|
|
33870
|
+
* Fresh signed URL for a recording's mixed MP4, or null. Resolve at point
|
|
33871
|
+
* of use — the URL expires after six hours.
|
|
33872
|
+
*/
|
|
33873
|
+
getRecordingVideoUrl: (recordingId) => recallService.getRecordingVideoUrl(recordingId)
|
|
33874
|
+
};
|
|
33875
|
+
|
|
33876
|
+
// src/index.ts
|
|
33652
33877
|
var ExuluJobs = {
|
|
33653
33878
|
redis: redisClient
|
|
33654
33879
|
};
|
|
@@ -33746,10 +33971,12 @@ var ExuluPython = {
|
|
|
33746
33971
|
ExuluDocumentProcessor,
|
|
33747
33972
|
ExuluEval,
|
|
33748
33973
|
ExuluJobs,
|
|
33974
|
+
ExuluModels,
|
|
33749
33975
|
ExuluOtel,
|
|
33750
33976
|
ExuluPython,
|
|
33751
33977
|
ExuluQueues,
|
|
33752
33978
|
ExuluReadApi,
|
|
33979
|
+
ExuluRecall,
|
|
33753
33980
|
ExuluReranker,
|
|
33754
33981
|
ExuluTool,
|
|
33755
33982
|
ExuluVariables,
|
package/dist/index.d.cts
CHANGED
|
@@ -2506,6 +2506,37 @@ declare function createAgenticRetrievalTool(opts: {
|
|
|
2506
2506
|
projectScope?: ProjectScope;
|
|
2507
2507
|
}): ExuluTool | undefined;
|
|
2508
2508
|
|
|
2509
|
+
/**
|
|
2510
|
+
* Model access for consuming projects.
|
|
2511
|
+
*
|
|
2512
|
+
* Exists because ExuluApp exposes no model route and resolveModel is internal,
|
|
2513
|
+
* so a consumer's only alternative was building its own provider against
|
|
2514
|
+
* PROXY_BASE_URL — which produces untagged calls that per-team LiteLLM budgets
|
|
2515
|
+
* cannot attribute.
|
|
2516
|
+
*/
|
|
2517
|
+
declare const ExuluModels: {
|
|
2518
|
+
/**
|
|
2519
|
+
* A LanguageModel bound to the tenant's LiteLLM proxy.
|
|
2520
|
+
*
|
|
2521
|
+
* Pass `userId` wherever one is known — it is what carries the caller's
|
|
2522
|
+
* identity tags into LiteLLM, and therefore what makes the spend visible to
|
|
2523
|
+
* that user's team budget. Resolution still succeeds without it; the calls
|
|
2524
|
+
* are simply unattributed.
|
|
2525
|
+
*/
|
|
2526
|
+
resolve: ({ modelId, userId, }: {
|
|
2527
|
+
modelId: string;
|
|
2528
|
+
userId?: number;
|
|
2529
|
+
}) => Promise<LanguageModel>;
|
|
2530
|
+
/**
|
|
2531
|
+
* Provider options for a constrained structured-output call.
|
|
2532
|
+
*
|
|
2533
|
+
* Gemini 3+ counts thinking tokens against maxOutputTokens, so a call with a
|
|
2534
|
+
* token cap returns an empty 200 unless reasoning is disabled. Returns
|
|
2535
|
+
* undefined for non-Gemini models.
|
|
2536
|
+
*/
|
|
2537
|
+
providerOptions: (model: LanguageModel | string) => Record<string, Record<string, string>> | undefined;
|
|
2538
|
+
};
|
|
2539
|
+
|
|
2509
2540
|
type JOB_STATUS = "completed" | "failed" | "delayed" | "active" | "waiting" | "paused" | "stuck" | "waiting_approval" | "filtered" | "cancelled";
|
|
2510
2541
|
declare const JOB_STATUS_ENUM: {
|
|
2511
2542
|
completed: string;
|
|
@@ -2526,9 +2557,19 @@ declare class CredentialInvalidError extends Error {
|
|
|
2526
2557
|
constructor(provider: string, reason?: string);
|
|
2527
2558
|
}
|
|
2528
2559
|
|
|
2560
|
+
/** Public Recall surface for consuming projects. */
|
|
2561
|
+
declare const ExuluRecall: {
|
|
2562
|
+
/**
|
|
2563
|
+
* Fresh signed URL for a recording's mixed MP4, or null. Resolve at point
|
|
2564
|
+
* of use — the URL expires after six hours.
|
|
2565
|
+
*/
|
|
2566
|
+
getRecordingVideoUrl: (recordingId: string) => Promise<string | null>;
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2529
2569
|
declare const ExuluJobs: {
|
|
2530
2570
|
redis: typeof redisClient;
|
|
2531
2571
|
};
|
|
2572
|
+
|
|
2532
2573
|
declare const ExuluDefaultTools: {
|
|
2533
2574
|
agentic: {
|
|
2534
2575
|
retrieval: {
|
|
@@ -2600,4 +2641,4 @@ declare const ExuluPython: {
|
|
|
2600
2641
|
instructions: typeof getPythonSetupInstructions;
|
|
2601
2642
|
};
|
|
2602
2643
|
|
|
2603
|
-
export { type AuditConfig, type AuditEvent, type AuditLogger, type ChunkerOperation, type ChunkerResponse, type CredentialField, CredentialInvalidError, type JOB_STATUS as EXULU_JOB_STATUS, JOB_STATUS_ENUM as EXULU_JOB_STATUS_ENUM, type STATISTICS_TYPE as EXULU_STATISTICS_TYPE, STATISTICS_TYPE_ENUM as EXULU_STATISTICS_TYPE_ENUM, type ExuluAgent, ExuluApp, type ExuluAuthConfig, ExuluAuthentication, ExuluChunkers, ExuluContext, type ExuluContextEmbedder, type ExuluCredentialsToolContext, ExuluDatabase, ExuluDefaultTools, ExuluDocumentProcessor, ExuluEval, type Item as ExuluItem, ExuluJobs, type ExuluOauthConfig, type ExuluOauthToolContext, ExuluOtel, ExuluPython, queues as ExuluQueues, ExuluReadApi, ExuluReranker, ExuluTool, type ExuluUserCredentialsConfig, ExuluVariables, type VectorSearchChunkResult, defaultChunker, enableLiteLLMClientMode, postgresClient };
|
|
2644
|
+
export { type AuditConfig, type AuditEvent, type AuditLogger, type ChunkerOperation, type ChunkerResponse, type CredentialField, CredentialInvalidError, type JOB_STATUS as EXULU_JOB_STATUS, JOB_STATUS_ENUM as EXULU_JOB_STATUS_ENUM, type STATISTICS_TYPE as EXULU_STATISTICS_TYPE, STATISTICS_TYPE_ENUM as EXULU_STATISTICS_TYPE_ENUM, type ExuluAgent, ExuluApp, type ExuluAuthConfig, ExuluAuthentication, ExuluChunkers, ExuluContext, type ExuluContextEmbedder, type ExuluCredentialsToolContext, ExuluDatabase, ExuluDefaultTools, ExuluDocumentProcessor, ExuluEval, type Item as ExuluItem, ExuluJobs, ExuluModels, type ExuluOauthConfig, type ExuluOauthToolContext, ExuluOtel, ExuluPython, queues as ExuluQueues, ExuluReadApi, ExuluRecall, ExuluReranker, ExuluTool, type ExuluUserCredentialsConfig, ExuluVariables, type VectorSearchChunkResult, defaultChunker, enableLiteLLMClientMode, postgresClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -2506,6 +2506,37 @@ declare function createAgenticRetrievalTool(opts: {
|
|
|
2506
2506
|
projectScope?: ProjectScope;
|
|
2507
2507
|
}): ExuluTool | undefined;
|
|
2508
2508
|
|
|
2509
|
+
/**
|
|
2510
|
+
* Model access for consuming projects.
|
|
2511
|
+
*
|
|
2512
|
+
* Exists because ExuluApp exposes no model route and resolveModel is internal,
|
|
2513
|
+
* so a consumer's only alternative was building its own provider against
|
|
2514
|
+
* PROXY_BASE_URL — which produces untagged calls that per-team LiteLLM budgets
|
|
2515
|
+
* cannot attribute.
|
|
2516
|
+
*/
|
|
2517
|
+
declare const ExuluModels: {
|
|
2518
|
+
/**
|
|
2519
|
+
* A LanguageModel bound to the tenant's LiteLLM proxy.
|
|
2520
|
+
*
|
|
2521
|
+
* Pass `userId` wherever one is known — it is what carries the caller's
|
|
2522
|
+
* identity tags into LiteLLM, and therefore what makes the spend visible to
|
|
2523
|
+
* that user's team budget. Resolution still succeeds without it; the calls
|
|
2524
|
+
* are simply unattributed.
|
|
2525
|
+
*/
|
|
2526
|
+
resolve: ({ modelId, userId, }: {
|
|
2527
|
+
modelId: string;
|
|
2528
|
+
userId?: number;
|
|
2529
|
+
}) => Promise<LanguageModel>;
|
|
2530
|
+
/**
|
|
2531
|
+
* Provider options for a constrained structured-output call.
|
|
2532
|
+
*
|
|
2533
|
+
* Gemini 3+ counts thinking tokens against maxOutputTokens, so a call with a
|
|
2534
|
+
* token cap returns an empty 200 unless reasoning is disabled. Returns
|
|
2535
|
+
* undefined for non-Gemini models.
|
|
2536
|
+
*/
|
|
2537
|
+
providerOptions: (model: LanguageModel | string) => Record<string, Record<string, string>> | undefined;
|
|
2538
|
+
};
|
|
2539
|
+
|
|
2509
2540
|
type JOB_STATUS = "completed" | "failed" | "delayed" | "active" | "waiting" | "paused" | "stuck" | "waiting_approval" | "filtered" | "cancelled";
|
|
2510
2541
|
declare const JOB_STATUS_ENUM: {
|
|
2511
2542
|
completed: string;
|
|
@@ -2526,9 +2557,19 @@ declare class CredentialInvalidError extends Error {
|
|
|
2526
2557
|
constructor(provider: string, reason?: string);
|
|
2527
2558
|
}
|
|
2528
2559
|
|
|
2560
|
+
/** Public Recall surface for consuming projects. */
|
|
2561
|
+
declare const ExuluRecall: {
|
|
2562
|
+
/**
|
|
2563
|
+
* Fresh signed URL for a recording's mixed MP4, or null. Resolve at point
|
|
2564
|
+
* of use — the URL expires after six hours.
|
|
2565
|
+
*/
|
|
2566
|
+
getRecordingVideoUrl: (recordingId: string) => Promise<string | null>;
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2529
2569
|
declare const ExuluJobs: {
|
|
2530
2570
|
redis: typeof redisClient;
|
|
2531
2571
|
};
|
|
2572
|
+
|
|
2532
2573
|
declare const ExuluDefaultTools: {
|
|
2533
2574
|
agentic: {
|
|
2534
2575
|
retrieval: {
|
|
@@ -2600,4 +2641,4 @@ declare const ExuluPython: {
|
|
|
2600
2641
|
instructions: typeof getPythonSetupInstructions;
|
|
2601
2642
|
};
|
|
2602
2643
|
|
|
2603
|
-
export { type AuditConfig, type AuditEvent, type AuditLogger, type ChunkerOperation, type ChunkerResponse, type CredentialField, CredentialInvalidError, type JOB_STATUS as EXULU_JOB_STATUS, JOB_STATUS_ENUM as EXULU_JOB_STATUS_ENUM, type STATISTICS_TYPE as EXULU_STATISTICS_TYPE, STATISTICS_TYPE_ENUM as EXULU_STATISTICS_TYPE_ENUM, type ExuluAgent, ExuluApp, type ExuluAuthConfig, ExuluAuthentication, ExuluChunkers, ExuluContext, type ExuluContextEmbedder, type ExuluCredentialsToolContext, ExuluDatabase, ExuluDefaultTools, ExuluDocumentProcessor, ExuluEval, type Item as ExuluItem, ExuluJobs, type ExuluOauthConfig, type ExuluOauthToolContext, ExuluOtel, ExuluPython, queues as ExuluQueues, ExuluReadApi, ExuluReranker, ExuluTool, type ExuluUserCredentialsConfig, ExuluVariables, type VectorSearchChunkResult, defaultChunker, enableLiteLLMClientMode, postgresClient };
|
|
2644
|
+
export { type AuditConfig, type AuditEvent, type AuditLogger, type ChunkerOperation, type ChunkerResponse, type CredentialField, CredentialInvalidError, type JOB_STATUS as EXULU_JOB_STATUS, JOB_STATUS_ENUM as EXULU_JOB_STATUS_ENUM, type STATISTICS_TYPE as EXULU_STATISTICS_TYPE, STATISTICS_TYPE_ENUM as EXULU_STATISTICS_TYPE_ENUM, type ExuluAgent, ExuluApp, type ExuluAuthConfig, ExuluAuthentication, ExuluChunkers, ExuluContext, type ExuluContextEmbedder, type ExuluCredentialsToolContext, ExuluDatabase, ExuluDefaultTools, ExuluDocumentProcessor, ExuluEval, type Item as ExuluItem, ExuluJobs, ExuluModels, type ExuluOauthConfig, type ExuluOauthToolContext, ExuluOtel, ExuluPython, queues as ExuluQueues, ExuluReadApi, ExuluRecall, ExuluReranker, ExuluTool, type ExuluUserCredentialsConfig, ExuluVariables, type VectorSearchChunkResult, defaultChunker, enableLiteLLMClientMode, postgresClient };
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,7 @@ import {
|
|
|
66
66
|
listS3ObjectsByPrefix,
|
|
67
67
|
listTagsByPrefix,
|
|
68
68
|
mapStreamErrorMessage,
|
|
69
|
+
microCallProviderOptions,
|
|
69
70
|
parseResetAt,
|
|
70
71
|
postgresClient,
|
|
71
72
|
provisionDefaultUserBudget,
|
|
@@ -87,7 +88,7 @@ import {
|
|
|
87
88
|
verifyCredentialNonce,
|
|
88
89
|
waitForLiteLLMReady,
|
|
89
90
|
withRetry
|
|
90
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-HA2ZSGOW.js";
|
|
91
92
|
import {
|
|
92
93
|
LiteLLMAdminError,
|
|
93
94
|
findLiteLLMModel,
|
|
@@ -2568,6 +2569,52 @@ var usersSchema = {
|
|
|
2568
2569
|
// triggered by the key with project_id_ for LiteLLM cost attribution.
|
|
2569
2570
|
name: "project",
|
|
2570
2571
|
type: "uuid"
|
|
2572
|
+
},
|
|
2573
|
+
// ── Lead capture (see lead-capture spec §5) ─────────────────────────────
|
|
2574
|
+
// Consent must be *provable* under Art. 7(1) GDPR: who agreed, when, and
|
|
2575
|
+
// to which wording. Hence a timestamp per consent and a version string
|
|
2576
|
+
// pointing at the exact text that was shown.
|
|
2577
|
+
{
|
|
2578
|
+
// Current state of the optional marketing consent.
|
|
2579
|
+
name: "marketing_consent",
|
|
2580
|
+
type: "boolean",
|
|
2581
|
+
default: false
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
// When marketing consent was GIVEN.
|
|
2585
|
+
name: "marketing_consent_at",
|
|
2586
|
+
type: "date"
|
|
2587
|
+
},
|
|
2588
|
+
{
|
|
2589
|
+
// When marketing consent was WITHDRAWN. Deliberately a separate column:
|
|
2590
|
+
// overwriting marketing_consent_at on withdrawal would destroy the grant
|
|
2591
|
+
// time, and with it the ability to prove the order of events if someone
|
|
2592
|
+
// later claims they were mailed after withdrawing.
|
|
2593
|
+
name: "marketing_consent_withdrawn_at",
|
|
2594
|
+
type: "date"
|
|
2595
|
+
},
|
|
2596
|
+
{
|
|
2597
|
+
// When the mandatory storage/processing consent was given.
|
|
2598
|
+
name: "processing_consent_at",
|
|
2599
|
+
type: "date"
|
|
2600
|
+
},
|
|
2601
|
+
{
|
|
2602
|
+
// When the user asked for erasure (Art. 17). The row survives so the
|
|
2603
|
+
// one-month deadline stays queryable even if the notification mail fails.
|
|
2604
|
+
name: "erasure_requested_at",
|
|
2605
|
+
type: "date"
|
|
2606
|
+
},
|
|
2607
|
+
{
|
|
2608
|
+
// Where the lead came from, e.g. "eu-ai-act-bot".
|
|
2609
|
+
name: "signup_source",
|
|
2610
|
+
type: "text"
|
|
2611
|
+
},
|
|
2612
|
+
{
|
|
2613
|
+
// Version of the consent wording that was actually displayed, e.g.
|
|
2614
|
+
// "2026-08-18". The wording itself lives in the frontend repo under
|
|
2615
|
+
// version control; this column points at it.
|
|
2616
|
+
name: "consent_version",
|
|
2617
|
+
type: "text"
|
|
2571
2618
|
}
|
|
2572
2619
|
]
|
|
2573
2620
|
};
|
|
@@ -2876,6 +2923,32 @@ var addCoreFields = (schema) => {
|
|
|
2876
2923
|
}
|
|
2877
2924
|
return schema;
|
|
2878
2925
|
};
|
|
2926
|
+
var otpSendAttemptsSchema = {
|
|
2927
|
+
type: "otp_send_attempts",
|
|
2928
|
+
name: {
|
|
2929
|
+
plural: "otp_send_attempts",
|
|
2930
|
+
singular: "otp_send_attempt"
|
|
2931
|
+
},
|
|
2932
|
+
fields: [
|
|
2933
|
+
{
|
|
2934
|
+
name: "key",
|
|
2935
|
+
type: "text",
|
|
2936
|
+
index: true,
|
|
2937
|
+
unique: true,
|
|
2938
|
+
required: true
|
|
2939
|
+
},
|
|
2940
|
+
{
|
|
2941
|
+
name: "count",
|
|
2942
|
+
type: "number",
|
|
2943
|
+
required: true
|
|
2944
|
+
},
|
|
2945
|
+
{
|
|
2946
|
+
name: "window_start",
|
|
2947
|
+
type: "date",
|
|
2948
|
+
required: true
|
|
2949
|
+
}
|
|
2950
|
+
]
|
|
2951
|
+
};
|
|
2879
2952
|
var coreSchemas = {
|
|
2880
2953
|
get: () => {
|
|
2881
2954
|
const license = checkLicense();
|
|
@@ -2896,7 +2969,8 @@ var coreSchemas = {
|
|
|
2896
2969
|
contextPresetsSchema: () => addCoreFields(contextPresetsSchema),
|
|
2897
2970
|
sharedArtifactsSchema: () => addCoreFields(sharedArtifactsSchema),
|
|
2898
2971
|
transcriptionJobsSchema: () => addCoreFields(transcriptionJobsSchema),
|
|
2899
|
-
imageGenerationsSchema: () => addCoreFields(imageGenerationsSchema)
|
|
2972
|
+
imageGenerationsSchema: () => addCoreFields(imageGenerationsSchema),
|
|
2973
|
+
otpSendAttemptsSchema: () => addCoreFields(otpSendAttemptsSchema)
|
|
2900
2974
|
};
|
|
2901
2975
|
if (license["agent-feedback"]) {
|
|
2902
2976
|
schemas.feedbackSchema = () => addCoreFields(feedbackSchema);
|
|
@@ -4731,7 +4805,7 @@ var ExuluContext2 = class {
|
|
|
4731
4805
|
}
|
|
4732
4806
|
console.log("[EXULU] context configuration", this.configuration);
|
|
4733
4807
|
let jobs = [];
|
|
4734
|
-
let
|
|
4808
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
4735
4809
|
if (this.processor) {
|
|
4736
4810
|
const processor = this.processor;
|
|
4737
4811
|
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -4753,12 +4827,12 @@ var ExuluContext2 = class {
|
|
|
4753
4827
|
...processorResult
|
|
4754
4828
|
});
|
|
4755
4829
|
if (processor.config?.generateEmbeddings) {
|
|
4756
|
-
|
|
4830
|
+
shouldGenerateEmbeddings2 = true;
|
|
4757
4831
|
}
|
|
4758
4832
|
}
|
|
4759
4833
|
}
|
|
4760
4834
|
}
|
|
4761
|
-
if (
|
|
4835
|
+
if (shouldGenerateEmbeddings2) {
|
|
4762
4836
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
4763
4837
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
4764
4838
|
item: {
|
|
@@ -4802,7 +4876,7 @@ var ExuluContext2 = class {
|
|
|
4802
4876
|
}).returning("id");
|
|
4803
4877
|
await mutation;
|
|
4804
4878
|
let jobs = [];
|
|
4805
|
-
let
|
|
4879
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
4806
4880
|
if (this.processor) {
|
|
4807
4881
|
const processor = this.processor;
|
|
4808
4882
|
if (processor && runProcessorOverwrite !== false && (runProcessorOverwrite || processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -4824,12 +4898,12 @@ var ExuluContext2 = class {
|
|
|
4824
4898
|
...processorResult
|
|
4825
4899
|
});
|
|
4826
4900
|
if (processor.config?.generateEmbeddings) {
|
|
4827
|
-
|
|
4901
|
+
shouldGenerateEmbeddings2 = true;
|
|
4828
4902
|
}
|
|
4829
4903
|
}
|
|
4830
4904
|
}
|
|
4831
4905
|
}
|
|
4832
|
-
if (
|
|
4906
|
+
if (shouldGenerateEmbeddings2) {
|
|
4833
4907
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
4834
4908
|
item: record,
|
|
4835
4909
|
// important we need to full record here with all fields for the embedder
|
|
@@ -6634,6 +6708,17 @@ var applyAgentGuestFieldTransforms = async (input) => {
|
|
|
6634
6708
|
return input;
|
|
6635
6709
|
};
|
|
6636
6710
|
|
|
6711
|
+
// src/graphql/mutations/should-generate-embeddings.ts
|
|
6712
|
+
var shouldGenerateEmbeddings = ({
|
|
6713
|
+
calculateVectors,
|
|
6714
|
+
operation,
|
|
6715
|
+
override
|
|
6716
|
+
}) => {
|
|
6717
|
+
if (override !== void 0) return override;
|
|
6718
|
+
if (calculateVectors === "always") return true;
|
|
6719
|
+
return operation === "create" ? calculateVectors === "onInsert" : calculateVectors === "onUpdate";
|
|
6720
|
+
};
|
|
6721
|
+
|
|
6637
6722
|
// src/graphql/mutations/index.ts
|
|
6638
6723
|
var VALID_RIGHTS_MODES = ["private", "users", "roles", "teams", "public"];
|
|
6639
6724
|
var postprocessDeletion = async ({
|
|
@@ -6702,7 +6787,9 @@ var postprocessUpdate = async ({
|
|
|
6702
6787
|
result,
|
|
6703
6788
|
user,
|
|
6704
6789
|
role,
|
|
6705
|
-
config
|
|
6790
|
+
config,
|
|
6791
|
+
operation,
|
|
6792
|
+
generateEmbeddings
|
|
6706
6793
|
}) => {
|
|
6707
6794
|
if (!result) {
|
|
6708
6795
|
return result;
|
|
@@ -6729,7 +6816,11 @@ var postprocessUpdate = async ({
|
|
|
6729
6816
|
if (!context.embedder) {
|
|
6730
6817
|
return result;
|
|
6731
6818
|
}
|
|
6732
|
-
if (
|
|
6819
|
+
if (shouldGenerateEmbeddings({
|
|
6820
|
+
calculateVectors: context.configuration.calculateVectors,
|
|
6821
|
+
operation,
|
|
6822
|
+
override: generateEmbeddings
|
|
6823
|
+
})) {
|
|
6733
6824
|
const { db } = await postgresClient();
|
|
6734
6825
|
console.log("[EXULU] Deleting chunks for item", result.id);
|
|
6735
6826
|
const exists = await context.chunksTableExists();
|
|
@@ -6979,7 +7070,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
6979
7070
|
result: results[0],
|
|
6980
7071
|
user: context.user.id,
|
|
6981
7072
|
role: context.user.role?.id,
|
|
6982
|
-
config
|
|
7073
|
+
config,
|
|
7074
|
+
operation: "create",
|
|
7075
|
+
generateEmbeddings: args.generateEmbeddings
|
|
6983
7076
|
});
|
|
6984
7077
|
return {
|
|
6985
7078
|
// Filter result to only include requested fields
|
|
@@ -7053,7 +7146,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7053
7146
|
result,
|
|
7054
7147
|
user: context.user.id,
|
|
7055
7148
|
role: context.user.role?.id,
|
|
7056
|
-
config
|
|
7149
|
+
config,
|
|
7150
|
+
operation: "update",
|
|
7151
|
+
generateEmbeddings: args.generateEmbeddings
|
|
7057
7152
|
});
|
|
7058
7153
|
return {
|
|
7059
7154
|
item: finalizeRequestedFields({
|
|
@@ -7117,7 +7212,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7117
7212
|
result,
|
|
7118
7213
|
user: context.user.id,
|
|
7119
7214
|
role: context.user.role?.id,
|
|
7120
|
-
config
|
|
7215
|
+
config,
|
|
7216
|
+
operation: "update",
|
|
7217
|
+
generateEmbeddings: args.generateEmbeddings
|
|
7121
7218
|
});
|
|
7122
7219
|
return {
|
|
7123
7220
|
item: finalizeRequestedFields({
|
|
@@ -7387,6 +7484,7 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7387
7484
|
};
|
|
7388
7485
|
}
|
|
7389
7486
|
query = applyFilters(query, args.where, table);
|
|
7487
|
+
query = applyAccessControl(table, query, context.user);
|
|
7390
7488
|
if (args.limit) {
|
|
7391
7489
|
query = query.limit(args.limit);
|
|
7392
7490
|
}
|
|
@@ -10762,6 +10860,32 @@ var renderTranscript = (segments, speakers) => {
|
|
|
10762
10860
|
return blocks.map((b) => `${b.speaker}: ${b.text}`).join("\n");
|
|
10763
10861
|
};
|
|
10764
10862
|
|
|
10863
|
+
// src/exulu/transcription/build-transcript-item.ts
|
|
10864
|
+
var buildTranscriptItemInput = ({
|
|
10865
|
+
row,
|
|
10866
|
+
title,
|
|
10867
|
+
speakers,
|
|
10868
|
+
transcriptText,
|
|
10869
|
+
rightsMode,
|
|
10870
|
+
isReSave
|
|
10871
|
+
}) => ({
|
|
10872
|
+
// Carrying the id on re-save makes context.createItem upsert in place.
|
|
10873
|
+
...isReSave && row.saved_item_id ? { id: row.saved_item_id } : {},
|
|
10874
|
+
name: title ?? row.title ?? "Transcript",
|
|
10875
|
+
transcript_text: transcriptText,
|
|
10876
|
+
audio_s3key: row.audio_s3key,
|
|
10877
|
+
language: row.language ?? void 0,
|
|
10878
|
+
duration_seconds: row.duration_seconds ?? void 0,
|
|
10879
|
+
speakers,
|
|
10880
|
+
raw_segments: row.raw_segments,
|
|
10881
|
+
// Recall meeting-bot post-processing results (null for Whisper jobs).
|
|
10882
|
+
post_processing: row.post_processing_outputs ?? void 0,
|
|
10883
|
+
// Handle for the meeting video; null for Whisper uploads.
|
|
10884
|
+
recall_recording_id: row.recall_recording_id ?? void 0,
|
|
10885
|
+
rights_mode: rightsMode,
|
|
10886
|
+
created_by: row.created_by
|
|
10887
|
+
});
|
|
10888
|
+
|
|
10765
10889
|
// src/exulu/transcription/service.ts
|
|
10766
10890
|
var TABLE = "transcription_jobs";
|
|
10767
10891
|
var log2 = (msg) => console.log(`[EXULU-TRANSCRIPTION] ${msg}`);
|
|
@@ -10954,21 +11078,14 @@ var transcriptionService = {
|
|
|
10954
11078
|
const transcriptText = renderTranscript(row.raw_segments, input.speakers);
|
|
10955
11079
|
const rightsMode = input.target_rights_mode ?? row.target_rights_mode ?? "private";
|
|
10956
11080
|
const isReSave = row.status === "saved" && !!row.saved_item_id;
|
|
10957
|
-
const itemInput = {
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
name: input.title ?? row.title ?? "Transcript",
|
|
10961
|
-
transcript_text: transcriptText,
|
|
10962
|
-
audio_s3key: row.audio_s3key,
|
|
10963
|
-
language: row.language ?? void 0,
|
|
10964
|
-
duration_seconds: row.duration_seconds ?? void 0,
|
|
11081
|
+
const itemInput = buildTranscriptItemInput({
|
|
11082
|
+
row,
|
|
11083
|
+
title: input.title,
|
|
10965
11084
|
speakers: input.speakers,
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
created_by: row.created_by
|
|
10971
|
-
};
|
|
11085
|
+
transcriptText,
|
|
11086
|
+
rightsMode,
|
|
11087
|
+
isReSave
|
|
11088
|
+
});
|
|
10972
11089
|
let item;
|
|
10973
11090
|
try {
|
|
10974
11091
|
const result = await context.createItem(
|
|
@@ -11206,25 +11323,34 @@ var recordingDurationSeconds = (rec) => {
|
|
|
11206
11323
|
}
|
|
11207
11324
|
return null;
|
|
11208
11325
|
};
|
|
11326
|
+
var RECORDING_RETENTION_HOURS = 2160;
|
|
11327
|
+
var buildCreateBotPayload = (input) => ({
|
|
11328
|
+
meeting_url: input.meeting_url,
|
|
11329
|
+
join_at: input.join_at,
|
|
11330
|
+
...input.bot_name ? { bot_name: input.bot_name } : {},
|
|
11331
|
+
recording_config: {
|
|
11332
|
+
video_mixed_mp4: {},
|
|
11333
|
+
video_mixed_layout: "speaker_view",
|
|
11334
|
+
participant_events: {},
|
|
11335
|
+
meeting_metadata: {},
|
|
11336
|
+
retention: { type: "timed", hours: RECORDING_RETENTION_HOURS }
|
|
11337
|
+
},
|
|
11338
|
+
...input.notifyChat ? {
|
|
11339
|
+
chat: {
|
|
11340
|
+
on_bot_join: {
|
|
11341
|
+
send_to: "everyone",
|
|
11342
|
+
message: input.notifyChat.message,
|
|
11343
|
+
pin: true
|
|
11344
|
+
}
|
|
11345
|
+
}
|
|
11346
|
+
} : {}
|
|
11347
|
+
});
|
|
11209
11348
|
var recallClient = {
|
|
11210
11349
|
isConfigured: () => !!recallApiKey() && !!process.env.RECALL_REGION,
|
|
11211
11350
|
/** POST /bot — schedule/launch a bot for a meeting. */
|
|
11212
11351
|
createBot: (input) => request2("/bot/", {
|
|
11213
11352
|
method: "POST",
|
|
11214
|
-
body: JSON.stringify(
|
|
11215
|
-
meeting_url: input.meeting_url,
|
|
11216
|
-
join_at: input.join_at,
|
|
11217
|
-
...input.bot_name ? { bot_name: input.bot_name } : {},
|
|
11218
|
-
...input.notifyChat ? {
|
|
11219
|
-
chat: {
|
|
11220
|
-
on_bot_join: {
|
|
11221
|
-
send_to: "everyone",
|
|
11222
|
-
message: input.notifyChat.message,
|
|
11223
|
-
pin: true
|
|
11224
|
-
}
|
|
11225
|
-
}
|
|
11226
|
-
} : {}
|
|
11227
|
-
})
|
|
11353
|
+
body: JSON.stringify(buildCreateBotPayload(input))
|
|
11228
11354
|
}),
|
|
11229
11355
|
/**
|
|
11230
11356
|
* POST /recording/{id}/create_transcript — start a post-meeting (async)
|
|
@@ -11539,6 +11665,28 @@ var recallService = {
|
|
|
11539
11665
|
}
|
|
11540
11666
|
return null;
|
|
11541
11667
|
},
|
|
11668
|
+
/**
|
|
11669
|
+
* Fresh signed URL for a recording's mixed MP4, or null when there is no
|
|
11670
|
+
* usable video (still processing, no video artifact, recording expired or
|
|
11671
|
+
* deleted).
|
|
11672
|
+
*
|
|
11673
|
+
* Never cache the result: the URL carries X-Amz-Expires=21600, so it dies
|
|
11674
|
+
* after six hours. Resolve at point of use.
|
|
11675
|
+
*/
|
|
11676
|
+
async getRecordingVideoUrl(recordingId) {
|
|
11677
|
+
try {
|
|
11678
|
+
const recording = await recallClient.retrieveRecording(recordingId);
|
|
11679
|
+
const video = recording?.media_shortcuts?.video_mixed;
|
|
11680
|
+
if (!video) return null;
|
|
11681
|
+
if (video.status?.code && video.status.code !== "done") return null;
|
|
11682
|
+
return video.data?.download_url ?? null;
|
|
11683
|
+
} catch (err) {
|
|
11684
|
+
log3(
|
|
11685
|
+
`could not resolve video url for recording ${recordingId}: ${err.message}`
|
|
11686
|
+
);
|
|
11687
|
+
return null;
|
|
11688
|
+
}
|
|
11689
|
+
},
|
|
11542
11690
|
/**
|
|
11543
11691
|
* Run the selected {prompt, agent} pairs against the transcript and store the
|
|
11544
11692
|
* results on the row. Idempotent (skips if outputs already exist). Each entry
|
|
@@ -12426,11 +12574,11 @@ function createSDL(tables, contexts, tools, config, evals) {
|
|
|
12426
12574
|
`;
|
|
12427
12575
|
}
|
|
12428
12576
|
mutationDefs += `
|
|
12429
|
-
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean): ${tableNameSingular}MutationPayload
|
|
12577
|
+
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
12430
12578
|
${tableNamePlural}CopyOneById(id: ID!): ${tableNameSingular}MutationPayload
|
|
12431
12579
|
|
|
12432
|
-
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input
|
|
12433
|
-
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input
|
|
12580
|
+
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input!, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
12581
|
+
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input!, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
12434
12582
|
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
12435
12583
|
${tableNamePlural}RemoveOne(where: JSON!): ${tableNameSingular}
|
|
12436
12584
|
`;
|
|
@@ -20817,7 +20965,11 @@ var transcriptionsContext = new ExuluContext2({
|
|
|
20817
20965
|
// Post-processing results carried from the transcription job at finalize:
|
|
20818
20966
|
// [{ prompt_id, agent_id, prompt_name, output, ran_at }]. Recall meeting
|
|
20819
20967
|
// transcripts only for now.
|
|
20820
|
-
{ name: "post_processing", type: "json", editable: false }
|
|
20968
|
+
{ name: "post_processing", type: "json", editable: false },
|
|
20969
|
+
// Link back to the Recall recording so the mixed video stays reachable
|
|
20970
|
+
// (resolve a fresh URL via ExuluRecall.getRecordingVideoUrl — it expires
|
|
20971
|
+
// after six hours). Null for Whisper uploads.
|
|
20972
|
+
{ name: "recall_recording_id", type: "text" }
|
|
20821
20973
|
],
|
|
20822
20974
|
sources: [],
|
|
20823
20975
|
active: true,
|
|
@@ -22146,6 +22298,12 @@ var ExuluReadApi = {
|
|
|
22146
22298
|
embedQuery
|
|
22147
22299
|
};
|
|
22148
22300
|
|
|
22301
|
+
// src/exulu/context-fields-for-sync.ts
|
|
22302
|
+
var contextFieldsForSync = (context) => (context.fields ?? []).filter((field) => !!field?.name && !!field?.type).map((field) => ({
|
|
22303
|
+
...field,
|
|
22304
|
+
name: field.type === "file" ? `${field.name}_s3key` : field.name
|
|
22305
|
+
}));
|
|
22306
|
+
|
|
22149
22307
|
// src/postgres/init-exulu-db.ts
|
|
22150
22308
|
var {
|
|
22151
22309
|
agentsSchema: agentsSchema3,
|
|
@@ -22174,7 +22332,8 @@ var {
|
|
|
22174
22332
|
promptFavoritesSchema: promptFavoritesSchema3,
|
|
22175
22333
|
transcriptionJobsSchema: transcriptionJobsSchema3,
|
|
22176
22334
|
imageGenerationsSchema: imageGenerationsSchema2,
|
|
22177
|
-
sharedArtifactsSchema: sharedArtifactsSchema2
|
|
22335
|
+
sharedArtifactsSchema: sharedArtifactsSchema2,
|
|
22336
|
+
otpSendAttemptsSchema: otpSendAttemptsSchema2
|
|
22178
22337
|
} = coreSchemas.get();
|
|
22179
22338
|
var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
22180
22339
|
for (const field of fields) {
|
|
@@ -22192,8 +22351,9 @@ var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
|
22192
22351
|
await knex.schema.alterTable(tableName, (table) => {
|
|
22193
22352
|
mapType(table, type, sanitizedName, defaultValue, unique);
|
|
22194
22353
|
});
|
|
22354
|
+
} else {
|
|
22355
|
+
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
22195
22356
|
}
|
|
22196
|
-
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
22197
22357
|
}
|
|
22198
22358
|
};
|
|
22199
22359
|
var migrateUserCredentialsDataColumn = async (knex) => {
|
|
@@ -22245,7 +22405,8 @@ var up = async function(knex) {
|
|
|
22245
22405
|
variablesSchema3(),
|
|
22246
22406
|
skillsSchema3(),
|
|
22247
22407
|
workflowTemplatesSchema3(),
|
|
22248
|
-
workflowTriggersSchema2()
|
|
22408
|
+
workflowTriggersSchema2(),
|
|
22409
|
+
otpSendAttemptsSchema2()
|
|
22249
22410
|
];
|
|
22250
22411
|
const createTable = async (schema) => {
|
|
22251
22412
|
if (!await knex.schema.hasTable(schema.name.plural)) {
|
|
@@ -22376,11 +22537,18 @@ var up = async function(knex) {
|
|
|
22376
22537
|
}
|
|
22377
22538
|
};
|
|
22378
22539
|
var contextDatabases = async (contexts) => {
|
|
22540
|
+
const { db: knex } = await postgresClient();
|
|
22379
22541
|
for (const context of contexts) {
|
|
22380
22542
|
const itemsTableExists = await context.tableExists();
|
|
22381
22543
|
if (!itemsTableExists) {
|
|
22382
22544
|
console.log("[EXULU] items table does not exist, creating it.");
|
|
22383
22545
|
await context.createItemsTable();
|
|
22546
|
+
} else {
|
|
22547
|
+
await addMissingFields(
|
|
22548
|
+
knex,
|
|
22549
|
+
getTableName(context.id),
|
|
22550
|
+
contextFieldsForSync(context)
|
|
22551
|
+
);
|
|
22384
22552
|
}
|
|
22385
22553
|
const chunksTableExists = await context.chunksTableExists();
|
|
22386
22554
|
if (!chunksTableExists && context.embedder) {
|
|
@@ -24237,6 +24405,47 @@ async function rerank(input) {
|
|
|
24237
24405
|
return resolved.rerank(query, items, { topN });
|
|
24238
24406
|
}
|
|
24239
24407
|
|
|
24408
|
+
// src/exulu/models/public.ts
|
|
24409
|
+
var ExuluModels = {
|
|
24410
|
+
/**
|
|
24411
|
+
* A LanguageModel bound to the tenant's LiteLLM proxy.
|
|
24412
|
+
*
|
|
24413
|
+
* Pass `userId` wherever one is known — it is what carries the caller's
|
|
24414
|
+
* identity tags into LiteLLM, and therefore what makes the spend visible to
|
|
24415
|
+
* that user's team budget. Resolution still succeeds without it; the calls
|
|
24416
|
+
* are simply unattributed.
|
|
24417
|
+
*/
|
|
24418
|
+
resolve: async ({
|
|
24419
|
+
modelId,
|
|
24420
|
+
userId
|
|
24421
|
+
}) => {
|
|
24422
|
+
let user;
|
|
24423
|
+
if (userId != null) {
|
|
24424
|
+
const { db } = await postgresClient();
|
|
24425
|
+
user = await db("users").where({ id: userId }).first();
|
|
24426
|
+
}
|
|
24427
|
+
const { languageModel } = await resolveModel({ modelId, user: user || void 0 });
|
|
24428
|
+
return languageModel;
|
|
24429
|
+
},
|
|
24430
|
+
/**
|
|
24431
|
+
* Provider options for a constrained structured-output call.
|
|
24432
|
+
*
|
|
24433
|
+
* Gemini 3+ counts thinking tokens against maxOutputTokens, so a call with a
|
|
24434
|
+
* token cap returns an empty 200 unless reasoning is disabled. Returns
|
|
24435
|
+
* undefined for non-Gemini models.
|
|
24436
|
+
*/
|
|
24437
|
+
providerOptions: (model) => microCallProviderOptions(model)
|
|
24438
|
+
};
|
|
24439
|
+
|
|
24440
|
+
// src/exulu/recall/public.ts
|
|
24441
|
+
var ExuluRecall = {
|
|
24442
|
+
/**
|
|
24443
|
+
* Fresh signed URL for a recording's mixed MP4, or null. Resolve at point
|
|
24444
|
+
* of use — the URL expires after six hours.
|
|
24445
|
+
*/
|
|
24446
|
+
getRecordingVideoUrl: (recordingId) => recallService.getRecordingVideoUrl(recordingId)
|
|
24447
|
+
};
|
|
24448
|
+
|
|
24240
24449
|
// src/index.ts
|
|
24241
24450
|
var ExuluJobs = {
|
|
24242
24451
|
redis: redisClient
|
|
@@ -24334,10 +24543,12 @@ export {
|
|
|
24334
24543
|
ExuluDocumentProcessor,
|
|
24335
24544
|
ExuluEval,
|
|
24336
24545
|
ExuluJobs,
|
|
24546
|
+
ExuluModels,
|
|
24337
24547
|
ExuluOtel,
|
|
24338
24548
|
ExuluPython,
|
|
24339
24549
|
queues as ExuluQueues,
|
|
24340
24550
|
ExuluReadApi,
|
|
24551
|
+
ExuluRecall,
|
|
24341
24552
|
ExuluReranker,
|
|
24342
24553
|
ExuluTool,
|
|
24343
24554
|
ExuluVariables,
|
|
@@ -24,17 +24,26 @@ pyannote.audio>=3.3.0
|
|
|
24
24
|
# Belt-and-suspenders: keep huggingface_hub on the 0.x line so pyannote 3.x's
|
|
25
25
|
# `use_auth_token=` calls keep working (1.x removed that kwarg → diarization off).
|
|
26
26
|
huggingface_hub<1.0
|
|
27
|
-
fastapi
|
|
28
27
|
uvicorn
|
|
29
28
|
python-multipart
|
|
30
29
|
requests
|
|
31
30
|
# LiteLLM proxy — only used when EXULU_USE_LITELLM=true. Always installed so
|
|
32
31
|
# the dep is ready when the env var is flipped. Pinned to a tested version;
|
|
33
32
|
# upgrade deliberately.
|
|
34
|
-
litellm[proxy]==1.
|
|
33
|
+
litellm[proxy]==1.97.0
|
|
34
|
+
# FastAPI ceiling — NOT cosmetic. litellm 1.97.0 declares fastapi>=0.136.3,<1.0
|
|
35
|
+
# but imports the private `get_flat_dependant` from fastapi.dependencies.utils
|
|
36
|
+
# (proxy/management_endpoints/management_v1/common.py:7), which FastAPI deleted
|
|
37
|
+
# in 0.140.7. Any resolve that picks 0.140.7+ yields a proxy that cannot start:
|
|
38
|
+
# "ImportError: cannot import name 'get_flat_dependant'". 0.140.6 is the last
|
|
39
|
+
# version that still has it. NOTE: fastapi is also a first-party dependency of
|
|
40
|
+
# ee/python/transcription/server.py (not just a litellm transitive). Once
|
|
41
|
+
# litellm ships its own ceiling, replace this constrained pin with a bare
|
|
42
|
+
# `fastapi` line — do not delete it entirely.
|
|
43
|
+
fastapi>=0.136.3,<0.140.7 # to confirm this pin is still needed: grep -r get_flat_dependant $(pip show litellm | awk '/Location/{print $2}')/litellm/proxy/
|
|
35
44
|
# Prisma Python client — required by LiteLLM proxy whenever a `database_url`
|
|
36
45
|
# is set in config.litellm.yaml (used for user/budget tracking, key
|
|
37
|
-
# management, etc.). NOT included in litellm[proxy] in 1.
|
|
46
|
+
# management, etc.). NOT included in litellm[proxy] in 1.97.0, hence the
|
|
38
47
|
# separate pin. setup.sh runs `prisma generate` against LiteLLM's bundled
|
|
39
48
|
# schema after pip install so the Python client module is materialized.
|
|
40
49
|
prisma==0.15.0
|