@exulu/backend 3.6.0 → 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 +196 -44
- package/dist/index.d.cts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +181 -45
- 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,
|
|
@@ -13714,7 +13716,7 @@ var ExuluContext2 = class {
|
|
|
13714
13716
|
}
|
|
13715
13717
|
console.log("[EXULU] context configuration", this.configuration);
|
|
13716
13718
|
let jobs = [];
|
|
13717
|
-
let
|
|
13719
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
13718
13720
|
if (this.processor) {
|
|
13719
13721
|
const processor = this.processor;
|
|
13720
13722
|
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -13736,12 +13738,12 @@ var ExuluContext2 = class {
|
|
|
13736
13738
|
...processorResult
|
|
13737
13739
|
});
|
|
13738
13740
|
if (processor.config?.generateEmbeddings) {
|
|
13739
|
-
|
|
13741
|
+
shouldGenerateEmbeddings2 = true;
|
|
13740
13742
|
}
|
|
13741
13743
|
}
|
|
13742
13744
|
}
|
|
13743
13745
|
}
|
|
13744
|
-
if (
|
|
13746
|
+
if (shouldGenerateEmbeddings2) {
|
|
13745
13747
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
13746
13748
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
13747
13749
|
item: {
|
|
@@ -13785,7 +13787,7 @@ var ExuluContext2 = class {
|
|
|
13785
13787
|
}).returning("id");
|
|
13786
13788
|
await mutation;
|
|
13787
13789
|
let jobs = [];
|
|
13788
|
-
let
|
|
13790
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
13789
13791
|
if (this.processor) {
|
|
13790
13792
|
const processor = this.processor;
|
|
13791
13793
|
if (processor && runProcessorOverwrite !== false && (runProcessorOverwrite || processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -13807,12 +13809,12 @@ var ExuluContext2 = class {
|
|
|
13807
13809
|
...processorResult
|
|
13808
13810
|
});
|
|
13809
13811
|
if (processor.config?.generateEmbeddings) {
|
|
13810
|
-
|
|
13812
|
+
shouldGenerateEmbeddings2 = true;
|
|
13811
13813
|
}
|
|
13812
13814
|
}
|
|
13813
13815
|
}
|
|
13814
13816
|
}
|
|
13815
|
-
if (
|
|
13817
|
+
if (shouldGenerateEmbeddings2) {
|
|
13816
13818
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
13817
13819
|
item: record,
|
|
13818
13820
|
// important we need to full record here with all fields for the embedder
|
|
@@ -15668,6 +15670,18 @@ var applyAgentGuestFieldTransforms = async (input) => {
|
|
|
15668
15670
|
return input;
|
|
15669
15671
|
};
|
|
15670
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
|
+
|
|
15671
15685
|
// src/graphql/mutations/index.ts
|
|
15672
15686
|
var VALID_RIGHTS_MODES = ["private", "users", "roles", "teams", "public"];
|
|
15673
15687
|
var postprocessDeletion = async ({
|
|
@@ -15736,7 +15750,9 @@ var postprocessUpdate = async ({
|
|
|
15736
15750
|
result,
|
|
15737
15751
|
user,
|
|
15738
15752
|
role,
|
|
15739
|
-
config
|
|
15753
|
+
config,
|
|
15754
|
+
operation,
|
|
15755
|
+
generateEmbeddings
|
|
15740
15756
|
}) => {
|
|
15741
15757
|
if (!result) {
|
|
15742
15758
|
return result;
|
|
@@ -15763,7 +15779,11 @@ var postprocessUpdate = async ({
|
|
|
15763
15779
|
if (!context.embedder) {
|
|
15764
15780
|
return result;
|
|
15765
15781
|
}
|
|
15766
|
-
if (
|
|
15782
|
+
if (shouldGenerateEmbeddings({
|
|
15783
|
+
calculateVectors: context.configuration.calculateVectors,
|
|
15784
|
+
operation,
|
|
15785
|
+
override: generateEmbeddings
|
|
15786
|
+
})) {
|
|
15767
15787
|
const { db: db2 } = await postgresClient();
|
|
15768
15788
|
console.log("[EXULU] Deleting chunks for item", result.id);
|
|
15769
15789
|
const exists = await context.chunksTableExists();
|
|
@@ -16013,7 +16033,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16013
16033
|
result: results[0],
|
|
16014
16034
|
user: context.user.id,
|
|
16015
16035
|
role: context.user.role?.id,
|
|
16016
|
-
config
|
|
16036
|
+
config,
|
|
16037
|
+
operation: "create",
|
|
16038
|
+
generateEmbeddings: args.generateEmbeddings
|
|
16017
16039
|
});
|
|
16018
16040
|
return {
|
|
16019
16041
|
// Filter result to only include requested fields
|
|
@@ -16087,7 +16109,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16087
16109
|
result,
|
|
16088
16110
|
user: context.user.id,
|
|
16089
16111
|
role: context.user.role?.id,
|
|
16090
|
-
config
|
|
16112
|
+
config,
|
|
16113
|
+
operation: "update",
|
|
16114
|
+
generateEmbeddings: args.generateEmbeddings
|
|
16091
16115
|
});
|
|
16092
16116
|
return {
|
|
16093
16117
|
item: finalizeRequestedFields({
|
|
@@ -16151,7 +16175,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16151
16175
|
result,
|
|
16152
16176
|
user: context.user.id,
|
|
16153
16177
|
role: context.user.role?.id,
|
|
16154
|
-
config
|
|
16178
|
+
config,
|
|
16179
|
+
operation: "update",
|
|
16180
|
+
generateEmbeddings: args.generateEmbeddings
|
|
16155
16181
|
});
|
|
16156
16182
|
return {
|
|
16157
16183
|
item: finalizeRequestedFields({
|
|
@@ -16421,6 +16447,7 @@ function createMutations(table, contexts, tools, config) {
|
|
|
16421
16447
|
};
|
|
16422
16448
|
}
|
|
16423
16449
|
query = applyFilters(query, args.where, table);
|
|
16450
|
+
query = applyAccessControl(table, query, context.user);
|
|
16424
16451
|
if (args.limit) {
|
|
16425
16452
|
query = query.limit(args.limit);
|
|
16426
16453
|
}
|
|
@@ -19861,6 +19888,33 @@ var renderTranscript = (segments, speakers) => {
|
|
|
19861
19888
|
return blocks.map((b) => `${b.speaker}: ${b.text}`).join("\n");
|
|
19862
19889
|
};
|
|
19863
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
|
+
|
|
19864
19918
|
// src/exulu/transcription/service.ts
|
|
19865
19919
|
var TABLE2 = "transcription_jobs";
|
|
19866
19920
|
var log3 = (msg) => console.log(`[EXULU-TRANSCRIPTION] ${msg}`);
|
|
@@ -20053,21 +20107,14 @@ var transcriptionService = {
|
|
|
20053
20107
|
const transcriptText = renderTranscript(row.raw_segments, input.speakers);
|
|
20054
20108
|
const rightsMode = input.target_rights_mode ?? row.target_rights_mode ?? "private";
|
|
20055
20109
|
const isReSave = row.status === "saved" && !!row.saved_item_id;
|
|
20056
|
-
const itemInput = {
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
name: input.title ?? row.title ?? "Transcript",
|
|
20060
|
-
transcript_text: transcriptText,
|
|
20061
|
-
audio_s3key: row.audio_s3key,
|
|
20062
|
-
language: row.language ?? void 0,
|
|
20063
|
-
duration_seconds: row.duration_seconds ?? void 0,
|
|
20110
|
+
const itemInput = buildTranscriptItemInput({
|
|
20111
|
+
row,
|
|
20112
|
+
title: input.title,
|
|
20064
20113
|
speakers: input.speakers,
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
created_by: row.created_by
|
|
20070
|
-
};
|
|
20114
|
+
transcriptText,
|
|
20115
|
+
rightsMode,
|
|
20116
|
+
isReSave
|
|
20117
|
+
});
|
|
20071
20118
|
let item;
|
|
20072
20119
|
try {
|
|
20073
20120
|
const result = await context.createItem(
|
|
@@ -20313,25 +20360,34 @@ var recordingDurationSeconds = (rec) => {
|
|
|
20313
20360
|
}
|
|
20314
20361
|
return null;
|
|
20315
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
|
+
});
|
|
20316
20385
|
var recallClient = {
|
|
20317
20386
|
isConfigured: () => !!recallApiKey() && !!process.env.RECALL_REGION,
|
|
20318
20387
|
/** POST /bot — schedule/launch a bot for a meeting. */
|
|
20319
20388
|
createBot: (input) => request2("/bot/", {
|
|
20320
20389
|
method: "POST",
|
|
20321
|
-
body: JSON.stringify(
|
|
20322
|
-
meeting_url: input.meeting_url,
|
|
20323
|
-
join_at: input.join_at,
|
|
20324
|
-
...input.bot_name ? { bot_name: input.bot_name } : {},
|
|
20325
|
-
...input.notifyChat ? {
|
|
20326
|
-
chat: {
|
|
20327
|
-
on_bot_join: {
|
|
20328
|
-
send_to: "everyone",
|
|
20329
|
-
message: input.notifyChat.message,
|
|
20330
|
-
pin: true
|
|
20331
|
-
}
|
|
20332
|
-
}
|
|
20333
|
-
} : {}
|
|
20334
|
-
})
|
|
20390
|
+
body: JSON.stringify(buildCreateBotPayload(input))
|
|
20335
20391
|
}),
|
|
20336
20392
|
/**
|
|
20337
20393
|
* POST /recording/{id}/create_transcript — start a post-meeting (async)
|
|
@@ -20647,6 +20703,28 @@ var recallService = {
|
|
|
20647
20703
|
}
|
|
20648
20704
|
return null;
|
|
20649
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
|
+
},
|
|
20650
20728
|
/**
|
|
20651
20729
|
* Run the selected {prompt, agent} pairs against the transcript and store the
|
|
20652
20730
|
* results on the row. Idempotent (skips if outputs already exist). Each entry
|
|
@@ -21542,11 +21620,11 @@ function createSDL(tables, contexts, tools, config, evals) {
|
|
|
21542
21620
|
`;
|
|
21543
21621
|
}
|
|
21544
21622
|
mutationDefs += `
|
|
21545
|
-
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean): ${tableNameSingular}MutationPayload
|
|
21623
|
+
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
21546
21624
|
${tableNamePlural}CopyOneById(id: ID!): ${tableNameSingular}MutationPayload
|
|
21547
21625
|
|
|
21548
|
-
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input
|
|
21549
|
-
${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
|
|
21550
21628
|
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
21551
21629
|
${tableNamePlural}RemoveOne(where: JSON!): ${tableNameSingular}
|
|
21552
21630
|
`;
|
|
@@ -30252,7 +30330,11 @@ var transcriptionsContext = new ExuluContext2({
|
|
|
30252
30330
|
// Post-processing results carried from the transcription job at finalize:
|
|
30253
30331
|
// [{ prompt_id, agent_id, prompt_name, output, ran_at }]. Recall meeting
|
|
30254
30332
|
// transcripts only for now.
|
|
30255
|
-
{ 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" }
|
|
30256
30338
|
],
|
|
30257
30339
|
sources: [],
|
|
30258
30340
|
active: true,
|
|
@@ -31606,6 +31688,16 @@ init_supervisor();
|
|
|
31606
31688
|
init_cjs_shims();
|
|
31607
31689
|
init_client();
|
|
31608
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();
|
|
31609
31701
|
var {
|
|
31610
31702
|
agentsSchema: agentsSchema3,
|
|
31611
31703
|
feedbackSchema: feedbackSchema3,
|
|
@@ -31652,8 +31744,9 @@ var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
|
31652
31744
|
await knex.schema.alterTable(tableName, (table) => {
|
|
31653
31745
|
mapType(table, type, sanitizedName, defaultValue, unique);
|
|
31654
31746
|
});
|
|
31747
|
+
} else {
|
|
31748
|
+
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
31655
31749
|
}
|
|
31656
|
-
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
31657
31750
|
}
|
|
31658
31751
|
};
|
|
31659
31752
|
var migrateUserCredentialsDataColumn = async (knex) => {
|
|
@@ -31837,11 +31930,18 @@ var up = async function(knex) {
|
|
|
31837
31930
|
}
|
|
31838
31931
|
};
|
|
31839
31932
|
var contextDatabases = async (contexts) => {
|
|
31933
|
+
const { db: knex } = await postgresClient();
|
|
31840
31934
|
for (const context of contexts) {
|
|
31841
31935
|
const itemsTableExists = await context.tableExists();
|
|
31842
31936
|
if (!itemsTableExists) {
|
|
31843
31937
|
console.log("[EXULU] items table does not exist, creating it.");
|
|
31844
31938
|
await context.createItemsTable();
|
|
31939
|
+
} else {
|
|
31940
|
+
await addMissingFields(
|
|
31941
|
+
knex,
|
|
31942
|
+
getTableName(context.id),
|
|
31943
|
+
contextFieldsForSync(context)
|
|
31944
|
+
);
|
|
31845
31945
|
}
|
|
31846
31946
|
const chunksTableExists = await context.chunksTableExists();
|
|
31847
31947
|
if (!chunksTableExists && context.embedder) {
|
|
@@ -33722,8 +33822,58 @@ async function rerank(input) {
|
|
|
33722
33822
|
|
|
33723
33823
|
// src/index.ts
|
|
33724
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
|
|
33725
33863
|
init_statistics();
|
|
33726
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
|
|
33727
33877
|
var ExuluJobs = {
|
|
33728
33878
|
redis: redisClient
|
|
33729
33879
|
};
|
|
@@ -33821,10 +33971,12 @@ var ExuluPython = {
|
|
|
33821
33971
|
ExuluDocumentProcessor,
|
|
33822
33972
|
ExuluEval,
|
|
33823
33973
|
ExuluJobs,
|
|
33974
|
+
ExuluModels,
|
|
33824
33975
|
ExuluOtel,
|
|
33825
33976
|
ExuluPython,
|
|
33826
33977
|
ExuluQueues,
|
|
33827
33978
|
ExuluReadApi,
|
|
33979
|
+
ExuluRecall,
|
|
33828
33980
|
ExuluReranker,
|
|
33829
33981
|
ExuluTool,
|
|
33830
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,
|
|
@@ -4804,7 +4805,7 @@ var ExuluContext2 = class {
|
|
|
4804
4805
|
}
|
|
4805
4806
|
console.log("[EXULU] context configuration", this.configuration);
|
|
4806
4807
|
let jobs = [];
|
|
4807
|
-
let
|
|
4808
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
4808
4809
|
if (this.processor) {
|
|
4809
4810
|
const processor = this.processor;
|
|
4810
4811
|
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -4826,12 +4827,12 @@ var ExuluContext2 = class {
|
|
|
4826
4827
|
...processorResult
|
|
4827
4828
|
});
|
|
4828
4829
|
if (processor.config?.generateEmbeddings) {
|
|
4829
|
-
|
|
4830
|
+
shouldGenerateEmbeddings2 = true;
|
|
4830
4831
|
}
|
|
4831
4832
|
}
|
|
4832
4833
|
}
|
|
4833
4834
|
}
|
|
4834
|
-
if (
|
|
4835
|
+
if (shouldGenerateEmbeddings2) {
|
|
4835
4836
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
4836
4837
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
4837
4838
|
item: {
|
|
@@ -4875,7 +4876,7 @@ var ExuluContext2 = class {
|
|
|
4875
4876
|
}).returning("id");
|
|
4876
4877
|
await mutation;
|
|
4877
4878
|
let jobs = [];
|
|
4878
|
-
let
|
|
4879
|
+
let shouldGenerateEmbeddings2 = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
4879
4880
|
if (this.processor) {
|
|
4880
4881
|
const processor = this.processor;
|
|
4881
4882
|
if (processor && runProcessorOverwrite !== false && (runProcessorOverwrite || processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
@@ -4897,12 +4898,12 @@ var ExuluContext2 = class {
|
|
|
4897
4898
|
...processorResult
|
|
4898
4899
|
});
|
|
4899
4900
|
if (processor.config?.generateEmbeddings) {
|
|
4900
|
-
|
|
4901
|
+
shouldGenerateEmbeddings2 = true;
|
|
4901
4902
|
}
|
|
4902
4903
|
}
|
|
4903
4904
|
}
|
|
4904
4905
|
}
|
|
4905
|
-
if (
|
|
4906
|
+
if (shouldGenerateEmbeddings2) {
|
|
4906
4907
|
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
4907
4908
|
item: record,
|
|
4908
4909
|
// important we need to full record here with all fields for the embedder
|
|
@@ -6707,6 +6708,17 @@ var applyAgentGuestFieldTransforms = async (input) => {
|
|
|
6707
6708
|
return input;
|
|
6708
6709
|
};
|
|
6709
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
|
+
|
|
6710
6722
|
// src/graphql/mutations/index.ts
|
|
6711
6723
|
var VALID_RIGHTS_MODES = ["private", "users", "roles", "teams", "public"];
|
|
6712
6724
|
var postprocessDeletion = async ({
|
|
@@ -6775,7 +6787,9 @@ var postprocessUpdate = async ({
|
|
|
6775
6787
|
result,
|
|
6776
6788
|
user,
|
|
6777
6789
|
role,
|
|
6778
|
-
config
|
|
6790
|
+
config,
|
|
6791
|
+
operation,
|
|
6792
|
+
generateEmbeddings
|
|
6779
6793
|
}) => {
|
|
6780
6794
|
if (!result) {
|
|
6781
6795
|
return result;
|
|
@@ -6802,7 +6816,11 @@ var postprocessUpdate = async ({
|
|
|
6802
6816
|
if (!context.embedder) {
|
|
6803
6817
|
return result;
|
|
6804
6818
|
}
|
|
6805
|
-
if (
|
|
6819
|
+
if (shouldGenerateEmbeddings({
|
|
6820
|
+
calculateVectors: context.configuration.calculateVectors,
|
|
6821
|
+
operation,
|
|
6822
|
+
override: generateEmbeddings
|
|
6823
|
+
})) {
|
|
6806
6824
|
const { db } = await postgresClient();
|
|
6807
6825
|
console.log("[EXULU] Deleting chunks for item", result.id);
|
|
6808
6826
|
const exists = await context.chunksTableExists();
|
|
@@ -7052,7 +7070,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7052
7070
|
result: results[0],
|
|
7053
7071
|
user: context.user.id,
|
|
7054
7072
|
role: context.user.role?.id,
|
|
7055
|
-
config
|
|
7073
|
+
config,
|
|
7074
|
+
operation: "create",
|
|
7075
|
+
generateEmbeddings: args.generateEmbeddings
|
|
7056
7076
|
});
|
|
7057
7077
|
return {
|
|
7058
7078
|
// Filter result to only include requested fields
|
|
@@ -7126,7 +7146,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7126
7146
|
result,
|
|
7127
7147
|
user: context.user.id,
|
|
7128
7148
|
role: context.user.role?.id,
|
|
7129
|
-
config
|
|
7149
|
+
config,
|
|
7150
|
+
operation: "update",
|
|
7151
|
+
generateEmbeddings: args.generateEmbeddings
|
|
7130
7152
|
});
|
|
7131
7153
|
return {
|
|
7132
7154
|
item: finalizeRequestedFields({
|
|
@@ -7190,7 +7212,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7190
7212
|
result,
|
|
7191
7213
|
user: context.user.id,
|
|
7192
7214
|
role: context.user.role?.id,
|
|
7193
|
-
config
|
|
7215
|
+
config,
|
|
7216
|
+
operation: "update",
|
|
7217
|
+
generateEmbeddings: args.generateEmbeddings
|
|
7194
7218
|
});
|
|
7195
7219
|
return {
|
|
7196
7220
|
item: finalizeRequestedFields({
|
|
@@ -7460,6 +7484,7 @@ function createMutations(table, contexts, tools, config) {
|
|
|
7460
7484
|
};
|
|
7461
7485
|
}
|
|
7462
7486
|
query = applyFilters(query, args.where, table);
|
|
7487
|
+
query = applyAccessControl(table, query, context.user);
|
|
7463
7488
|
if (args.limit) {
|
|
7464
7489
|
query = query.limit(args.limit);
|
|
7465
7490
|
}
|
|
@@ -10835,6 +10860,32 @@ var renderTranscript = (segments, speakers) => {
|
|
|
10835
10860
|
return blocks.map((b) => `${b.speaker}: ${b.text}`).join("\n");
|
|
10836
10861
|
};
|
|
10837
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
|
+
|
|
10838
10889
|
// src/exulu/transcription/service.ts
|
|
10839
10890
|
var TABLE = "transcription_jobs";
|
|
10840
10891
|
var log2 = (msg) => console.log(`[EXULU-TRANSCRIPTION] ${msg}`);
|
|
@@ -11027,21 +11078,14 @@ var transcriptionService = {
|
|
|
11027
11078
|
const transcriptText = renderTranscript(row.raw_segments, input.speakers);
|
|
11028
11079
|
const rightsMode = input.target_rights_mode ?? row.target_rights_mode ?? "private";
|
|
11029
11080
|
const isReSave = row.status === "saved" && !!row.saved_item_id;
|
|
11030
|
-
const itemInput = {
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
name: input.title ?? row.title ?? "Transcript",
|
|
11034
|
-
transcript_text: transcriptText,
|
|
11035
|
-
audio_s3key: row.audio_s3key,
|
|
11036
|
-
language: row.language ?? void 0,
|
|
11037
|
-
duration_seconds: row.duration_seconds ?? void 0,
|
|
11081
|
+
const itemInput = buildTranscriptItemInput({
|
|
11082
|
+
row,
|
|
11083
|
+
title: input.title,
|
|
11038
11084
|
speakers: input.speakers,
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
created_by: row.created_by
|
|
11044
|
-
};
|
|
11085
|
+
transcriptText,
|
|
11086
|
+
rightsMode,
|
|
11087
|
+
isReSave
|
|
11088
|
+
});
|
|
11045
11089
|
let item;
|
|
11046
11090
|
try {
|
|
11047
11091
|
const result = await context.createItem(
|
|
@@ -11279,25 +11323,34 @@ var recordingDurationSeconds = (rec) => {
|
|
|
11279
11323
|
}
|
|
11280
11324
|
return null;
|
|
11281
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
|
+
});
|
|
11282
11348
|
var recallClient = {
|
|
11283
11349
|
isConfigured: () => !!recallApiKey() && !!process.env.RECALL_REGION,
|
|
11284
11350
|
/** POST /bot — schedule/launch a bot for a meeting. */
|
|
11285
11351
|
createBot: (input) => request2("/bot/", {
|
|
11286
11352
|
method: "POST",
|
|
11287
|
-
body: JSON.stringify(
|
|
11288
|
-
meeting_url: input.meeting_url,
|
|
11289
|
-
join_at: input.join_at,
|
|
11290
|
-
...input.bot_name ? { bot_name: input.bot_name } : {},
|
|
11291
|
-
...input.notifyChat ? {
|
|
11292
|
-
chat: {
|
|
11293
|
-
on_bot_join: {
|
|
11294
|
-
send_to: "everyone",
|
|
11295
|
-
message: input.notifyChat.message,
|
|
11296
|
-
pin: true
|
|
11297
|
-
}
|
|
11298
|
-
}
|
|
11299
|
-
} : {}
|
|
11300
|
-
})
|
|
11353
|
+
body: JSON.stringify(buildCreateBotPayload(input))
|
|
11301
11354
|
}),
|
|
11302
11355
|
/**
|
|
11303
11356
|
* POST /recording/{id}/create_transcript — start a post-meeting (async)
|
|
@@ -11612,6 +11665,28 @@ var recallService = {
|
|
|
11612
11665
|
}
|
|
11613
11666
|
return null;
|
|
11614
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
|
+
},
|
|
11615
11690
|
/**
|
|
11616
11691
|
* Run the selected {prompt, agent} pairs against the transcript and store the
|
|
11617
11692
|
* results on the row. Idempotent (skips if outputs already exist). Each entry
|
|
@@ -12499,11 +12574,11 @@ function createSDL(tables, contexts, tools, config, evals) {
|
|
|
12499
12574
|
`;
|
|
12500
12575
|
}
|
|
12501
12576
|
mutationDefs += `
|
|
12502
|
-
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean): ${tableNameSingular}MutationPayload
|
|
12577
|
+
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean, generateEmbeddings: Boolean): ${tableNameSingular}MutationPayload
|
|
12503
12578
|
${tableNamePlural}CopyOneById(id: ID!): ${tableNameSingular}MutationPayload
|
|
12504
12579
|
|
|
12505
|
-
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input
|
|
12506
|
-
${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
|
|
12507
12582
|
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
12508
12583
|
${tableNamePlural}RemoveOne(where: JSON!): ${tableNameSingular}
|
|
12509
12584
|
`;
|
|
@@ -20890,7 +20965,11 @@ var transcriptionsContext = new ExuluContext2({
|
|
|
20890
20965
|
// Post-processing results carried from the transcription job at finalize:
|
|
20891
20966
|
// [{ prompt_id, agent_id, prompt_name, output, ran_at }]. Recall meeting
|
|
20892
20967
|
// transcripts only for now.
|
|
20893
|
-
{ 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" }
|
|
20894
20973
|
],
|
|
20895
20974
|
sources: [],
|
|
20896
20975
|
active: true,
|
|
@@ -22219,6 +22298,12 @@ var ExuluReadApi = {
|
|
|
22219
22298
|
embedQuery
|
|
22220
22299
|
};
|
|
22221
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
|
+
|
|
22222
22307
|
// src/postgres/init-exulu-db.ts
|
|
22223
22308
|
var {
|
|
22224
22309
|
agentsSchema: agentsSchema3,
|
|
@@ -22266,8 +22351,9 @@ var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
|
22266
22351
|
await knex.schema.alterTable(tableName, (table) => {
|
|
22267
22352
|
mapType(table, type, sanitizedName, defaultValue, unique);
|
|
22268
22353
|
});
|
|
22354
|
+
} else {
|
|
22355
|
+
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
22269
22356
|
}
|
|
22270
|
-
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
22271
22357
|
}
|
|
22272
22358
|
};
|
|
22273
22359
|
var migrateUserCredentialsDataColumn = async (knex) => {
|
|
@@ -22451,11 +22537,18 @@ var up = async function(knex) {
|
|
|
22451
22537
|
}
|
|
22452
22538
|
};
|
|
22453
22539
|
var contextDatabases = async (contexts) => {
|
|
22540
|
+
const { db: knex } = await postgresClient();
|
|
22454
22541
|
for (const context of contexts) {
|
|
22455
22542
|
const itemsTableExists = await context.tableExists();
|
|
22456
22543
|
if (!itemsTableExists) {
|
|
22457
22544
|
console.log("[EXULU] items table does not exist, creating it.");
|
|
22458
22545
|
await context.createItemsTable();
|
|
22546
|
+
} else {
|
|
22547
|
+
await addMissingFields(
|
|
22548
|
+
knex,
|
|
22549
|
+
getTableName(context.id),
|
|
22550
|
+
contextFieldsForSync(context)
|
|
22551
|
+
);
|
|
22459
22552
|
}
|
|
22460
22553
|
const chunksTableExists = await context.chunksTableExists();
|
|
22461
22554
|
if (!chunksTableExists && context.embedder) {
|
|
@@ -24312,6 +24405,47 @@ async function rerank(input) {
|
|
|
24312
24405
|
return resolved.rerank(query, items, { topN });
|
|
24313
24406
|
}
|
|
24314
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
|
+
|
|
24315
24449
|
// src/index.ts
|
|
24316
24450
|
var ExuluJobs = {
|
|
24317
24451
|
redis: redisClient
|
|
@@ -24409,10 +24543,12 @@ export {
|
|
|
24409
24543
|
ExuluDocumentProcessor,
|
|
24410
24544
|
ExuluEval,
|
|
24411
24545
|
ExuluJobs,
|
|
24546
|
+
ExuluModels,
|
|
24412
24547
|
ExuluOtel,
|
|
24413
24548
|
ExuluPython,
|
|
24414
24549
|
queues as ExuluQueues,
|
|
24415
24550
|
ExuluReadApi,
|
|
24551
|
+
ExuluRecall,
|
|
24416
24552
|
ExuluReranker,
|
|
24417
24553
|
ExuluTool,
|
|
24418
24554
|
ExuluVariables,
|