@radaros/core 0.3.6 → 0.3.7
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/index.d.ts +316 -12
- package/dist/index.js +1148 -76
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -65,9 +65,11 @@ var InMemoryStorage = class {
|
|
|
65
65
|
// src/session/session-manager.ts
|
|
66
66
|
var NAMESPACE = "sessions";
|
|
67
67
|
var SessionManager = class {
|
|
68
|
-
constructor(storage) {
|
|
68
|
+
constructor(storage, config) {
|
|
69
69
|
this.storage = storage;
|
|
70
|
+
this.maxMessages = config?.maxMessages;
|
|
70
71
|
}
|
|
72
|
+
maxMessages;
|
|
71
73
|
async getOrCreate(sessionId, userId) {
|
|
72
74
|
const existing = await this.storage.get(NAMESPACE, sessionId);
|
|
73
75
|
if (existing) return existing;
|
|
@@ -83,16 +85,21 @@ var SessionManager = class {
|
|
|
83
85
|
return session;
|
|
84
86
|
}
|
|
85
87
|
async appendMessage(sessionId, msg) {
|
|
86
|
-
|
|
87
|
-
session.messages.push(msg);
|
|
88
|
-
session.updatedAt = /* @__PURE__ */ new Date();
|
|
89
|
-
await this.storage.set(NAMESPACE, sessionId, session);
|
|
88
|
+
return this.appendMessages(sessionId, [msg]);
|
|
90
89
|
}
|
|
91
90
|
async appendMessages(sessionId, msgs) {
|
|
92
91
|
const session = await this.getOrCreate(sessionId);
|
|
93
92
|
session.messages.push(...msgs);
|
|
93
|
+
let overflow = [];
|
|
94
|
+
if (this.maxMessages && session.messages.length > this.maxMessages) {
|
|
95
|
+
overflow = session.messages.splice(
|
|
96
|
+
0,
|
|
97
|
+
session.messages.length - this.maxMessages
|
|
98
|
+
);
|
|
99
|
+
}
|
|
94
100
|
session.updatedAt = /* @__PURE__ */ new Date();
|
|
95
101
|
await this.storage.set(NAMESPACE, sessionId, session);
|
|
102
|
+
return { overflow };
|
|
96
103
|
}
|
|
97
104
|
async getHistory(sessionId, limit) {
|
|
98
105
|
const session = await this.storage.get(NAMESPACE, sessionId);
|
|
@@ -782,7 +789,9 @@ var Agent = class {
|
|
|
782
789
|
if (typeof storage.initialize === "function") {
|
|
783
790
|
this.storageInitPromise = storage.initialize();
|
|
784
791
|
}
|
|
785
|
-
this.sessionManager = new SessionManager(storage
|
|
792
|
+
this.sessionManager = new SessionManager(storage, {
|
|
793
|
+
maxMessages: config.numHistoryRuns ? config.numHistoryRuns * 2 : void 0
|
|
794
|
+
});
|
|
786
795
|
this.logger = new Logger({
|
|
787
796
|
level: config.logLevel ?? "silent",
|
|
788
797
|
prefix: config.name
|
|
@@ -844,26 +853,24 @@ var Agent = class {
|
|
|
844
853
|
}
|
|
845
854
|
}
|
|
846
855
|
}
|
|
847
|
-
|
|
856
|
+
const newMessages = [
|
|
848
857
|
{ role: "user", content: inputText },
|
|
849
858
|
{ role: "assistant", content: output.text }
|
|
850
|
-
]
|
|
859
|
+
];
|
|
860
|
+
const { overflow } = await this.sessionManager.appendMessages(
|
|
861
|
+
sessionId,
|
|
862
|
+
newMessages
|
|
863
|
+
);
|
|
851
864
|
await this.sessionManager.updateState(sessionId, ctx.sessionState);
|
|
852
|
-
if (this.config.memory) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
]);
|
|
865
|
+
if (this.config.memory && overflow.length > 0) {
|
|
866
|
+
this.config.memory.summarize(sessionId, overflow, this.config.model).catch(
|
|
867
|
+
(e) => this.logger.warn(`Memory summarization failed: ${e}`)
|
|
868
|
+
);
|
|
857
869
|
}
|
|
858
870
|
if (this.config.userMemory && userId) {
|
|
859
|
-
this.config.userMemory.extractAndStore(
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
{ role: "user", content: inputText },
|
|
863
|
-
{ role: "assistant", content: output.text }
|
|
864
|
-
],
|
|
865
|
-
this.config.model
|
|
866
|
-
).catch((e) => this.logger.warn(`UserMemory extraction failed: ${e}`));
|
|
871
|
+
this.config.userMemory.extractAndStore(userId, newMessages, this.config.model).catch(
|
|
872
|
+
(e) => this.logger.warn(`UserMemory extraction failed: ${e}`)
|
|
873
|
+
);
|
|
867
874
|
}
|
|
868
875
|
if (this.config.hooks?.afterRun) {
|
|
869
876
|
await this.config.hooks.afterRun(ctx, output);
|
|
@@ -956,26 +963,24 @@ var Agent = class {
|
|
|
956
963
|
throw err;
|
|
957
964
|
} finally {
|
|
958
965
|
if (streamOk) {
|
|
959
|
-
|
|
966
|
+
const newMessages = [
|
|
960
967
|
{ role: "user", content: inputText },
|
|
961
968
|
{ role: "assistant", content: fullText }
|
|
962
|
-
]
|
|
969
|
+
];
|
|
970
|
+
const { overflow } = await this.sessionManager.appendMessages(
|
|
971
|
+
sessionId,
|
|
972
|
+
newMessages
|
|
973
|
+
);
|
|
963
974
|
await this.sessionManager.updateState(sessionId, ctx.sessionState);
|
|
964
|
-
if (this.config.memory) {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
]);
|
|
975
|
+
if (this.config.memory && overflow.length > 0) {
|
|
976
|
+
this.config.memory.summarize(sessionId, overflow, this.config.model).catch(
|
|
977
|
+
(e) => this.logger.warn(`Memory summarization failed: ${e}`)
|
|
978
|
+
);
|
|
969
979
|
}
|
|
970
980
|
if (this.config.userMemory && userId) {
|
|
971
|
-
this.config.userMemory.extractAndStore(
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
{ role: "user", content: inputText },
|
|
975
|
-
{ role: "assistant", content: fullText }
|
|
976
|
-
],
|
|
977
|
-
this.config.model
|
|
978
|
-
).catch((e) => this.logger.warn(`UserMemory extraction failed: ${e}`));
|
|
981
|
+
this.config.userMemory.extractAndStore(userId, newMessages, this.config.model).catch(
|
|
982
|
+
(e) => this.logger.warn(`UserMemory extraction failed: ${e}`)
|
|
983
|
+
);
|
|
979
984
|
}
|
|
980
985
|
this.eventBus.emit("run.complete", {
|
|
981
986
|
runId: ctx.runId,
|
|
@@ -3879,15 +3884,235 @@ var GoogleEmbedding = class {
|
|
|
3879
3884
|
|
|
3880
3885
|
// src/knowledge/knowledge-base.ts
|
|
3881
3886
|
import { z } from "zod";
|
|
3887
|
+
|
|
3888
|
+
// src/vector/bm25.ts
|
|
3889
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
3890
|
+
"a",
|
|
3891
|
+
"an",
|
|
3892
|
+
"and",
|
|
3893
|
+
"are",
|
|
3894
|
+
"as",
|
|
3895
|
+
"at",
|
|
3896
|
+
"be",
|
|
3897
|
+
"but",
|
|
3898
|
+
"by",
|
|
3899
|
+
"for",
|
|
3900
|
+
"from",
|
|
3901
|
+
"had",
|
|
3902
|
+
"has",
|
|
3903
|
+
"have",
|
|
3904
|
+
"he",
|
|
3905
|
+
"her",
|
|
3906
|
+
"his",
|
|
3907
|
+
"how",
|
|
3908
|
+
"i",
|
|
3909
|
+
"in",
|
|
3910
|
+
"is",
|
|
3911
|
+
"it",
|
|
3912
|
+
"its",
|
|
3913
|
+
"my",
|
|
3914
|
+
"no",
|
|
3915
|
+
"not",
|
|
3916
|
+
"of",
|
|
3917
|
+
"on",
|
|
3918
|
+
"or",
|
|
3919
|
+
"our",
|
|
3920
|
+
"she",
|
|
3921
|
+
"so",
|
|
3922
|
+
"than",
|
|
3923
|
+
"that",
|
|
3924
|
+
"the",
|
|
3925
|
+
"their",
|
|
3926
|
+
"them",
|
|
3927
|
+
"then",
|
|
3928
|
+
"there",
|
|
3929
|
+
"these",
|
|
3930
|
+
"they",
|
|
3931
|
+
"this",
|
|
3932
|
+
"to",
|
|
3933
|
+
"up",
|
|
3934
|
+
"was",
|
|
3935
|
+
"we",
|
|
3936
|
+
"were",
|
|
3937
|
+
"what",
|
|
3938
|
+
"when",
|
|
3939
|
+
"which",
|
|
3940
|
+
"who",
|
|
3941
|
+
"will",
|
|
3942
|
+
"with",
|
|
3943
|
+
"you",
|
|
3944
|
+
"your"
|
|
3945
|
+
]);
|
|
3946
|
+
function tokenize(text) {
|
|
3947
|
+
return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length > 1 && !STOP_WORDS.has(t));
|
|
3948
|
+
}
|
|
3949
|
+
var BM25Index = class {
|
|
3950
|
+
docs = /* @__PURE__ */ new Map();
|
|
3951
|
+
docFreqs = /* @__PURE__ */ new Map();
|
|
3952
|
+
avgDocLength = 0;
|
|
3953
|
+
/** BM25 free parameter: term frequency saturation. */
|
|
3954
|
+
k1;
|
|
3955
|
+
/** BM25 free parameter: document length normalization. */
|
|
3956
|
+
b;
|
|
3957
|
+
constructor(opts) {
|
|
3958
|
+
this.k1 = opts?.k1 ?? 1.5;
|
|
3959
|
+
this.b = opts?.b ?? 0.75;
|
|
3960
|
+
}
|
|
3961
|
+
get size() {
|
|
3962
|
+
return this.docs.size;
|
|
3963
|
+
}
|
|
3964
|
+
add(doc) {
|
|
3965
|
+
this.remove(doc.id);
|
|
3966
|
+
const tokens = tokenize(doc.content);
|
|
3967
|
+
const termFreqs = /* @__PURE__ */ new Map();
|
|
3968
|
+
for (const token of tokens) {
|
|
3969
|
+
termFreqs.set(token, (termFreqs.get(token) ?? 0) + 1);
|
|
3970
|
+
}
|
|
3971
|
+
const entry = {
|
|
3972
|
+
id: doc.id,
|
|
3973
|
+
content: doc.content,
|
|
3974
|
+
metadata: doc.metadata,
|
|
3975
|
+
termFreqs,
|
|
3976
|
+
length: tokens.length
|
|
3977
|
+
};
|
|
3978
|
+
this.docs.set(doc.id, entry);
|
|
3979
|
+
for (const term of termFreqs.keys()) {
|
|
3980
|
+
this.docFreqs.set(term, (this.docFreqs.get(term) ?? 0) + 1);
|
|
3981
|
+
}
|
|
3982
|
+
this.recomputeAvgLength();
|
|
3983
|
+
}
|
|
3984
|
+
addBatch(docs) {
|
|
3985
|
+
for (const doc of docs) {
|
|
3986
|
+
this.add(doc);
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
remove(id) {
|
|
3990
|
+
const existing = this.docs.get(id);
|
|
3991
|
+
if (!existing) return;
|
|
3992
|
+
for (const term of existing.termFreqs.keys()) {
|
|
3993
|
+
const count = this.docFreqs.get(term) ?? 1;
|
|
3994
|
+
if (count <= 1) {
|
|
3995
|
+
this.docFreqs.delete(term);
|
|
3996
|
+
} else {
|
|
3997
|
+
this.docFreqs.set(term, count - 1);
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
this.docs.delete(id);
|
|
4001
|
+
this.recomputeAvgLength();
|
|
4002
|
+
}
|
|
4003
|
+
clear() {
|
|
4004
|
+
this.docs.clear();
|
|
4005
|
+
this.docFreqs.clear();
|
|
4006
|
+
this.avgDocLength = 0;
|
|
4007
|
+
}
|
|
4008
|
+
search(query, opts) {
|
|
4009
|
+
const queryTokens = tokenize(query);
|
|
4010
|
+
if (queryTokens.length === 0) return [];
|
|
4011
|
+
const N = this.docs.size;
|
|
4012
|
+
if (N === 0) return [];
|
|
4013
|
+
const topK = opts?.topK ?? 10;
|
|
4014
|
+
const results = [];
|
|
4015
|
+
for (const doc of this.docs.values()) {
|
|
4016
|
+
if (opts?.filter) {
|
|
4017
|
+
let match = true;
|
|
4018
|
+
for (const [k, v] of Object.entries(opts.filter)) {
|
|
4019
|
+
if (doc.metadata?.[k] !== v) {
|
|
4020
|
+
match = false;
|
|
4021
|
+
break;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
if (!match) continue;
|
|
4025
|
+
}
|
|
4026
|
+
let score = 0;
|
|
4027
|
+
for (const term of queryTokens) {
|
|
4028
|
+
const tf = doc.termFreqs.get(term) ?? 0;
|
|
4029
|
+
if (tf === 0) continue;
|
|
4030
|
+
const df = this.docFreqs.get(term) ?? 0;
|
|
4031
|
+
const idf = Math.log(1 + (N - df + 0.5) / (df + 0.5));
|
|
4032
|
+
const numerator = tf * (this.k1 + 1);
|
|
4033
|
+
const denominator = tf + this.k1 * (1 - this.b + this.b * (doc.length / this.avgDocLength));
|
|
4034
|
+
score += idf * (numerator / denominator);
|
|
4035
|
+
}
|
|
4036
|
+
if (score > 0 && (opts?.minScore == null || score >= opts.minScore)) {
|
|
4037
|
+
results.push({
|
|
4038
|
+
id: doc.id,
|
|
4039
|
+
content: doc.content,
|
|
4040
|
+
score,
|
|
4041
|
+
metadata: doc.metadata
|
|
4042
|
+
});
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
results.sort((a, b) => b.score - a.score);
|
|
4046
|
+
return results.slice(0, topK);
|
|
4047
|
+
}
|
|
4048
|
+
recomputeAvgLength() {
|
|
4049
|
+
if (this.docs.size === 0) {
|
|
4050
|
+
this.avgDocLength = 0;
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
let total = 0;
|
|
4054
|
+
for (const doc of this.docs.values()) {
|
|
4055
|
+
total += doc.length;
|
|
4056
|
+
}
|
|
4057
|
+
this.avgDocLength = total / this.docs.size;
|
|
4058
|
+
}
|
|
4059
|
+
};
|
|
4060
|
+
|
|
4061
|
+
// src/vector/rrf.ts
|
|
4062
|
+
function reciprocalRankFusion(rankedLists, options) {
|
|
4063
|
+
const k = options?.k ?? 60;
|
|
4064
|
+
const topK = options?.topK ?? 10;
|
|
4065
|
+
const weights = options?.weights ?? rankedLists.map(() => 1);
|
|
4066
|
+
const fusedScores = /* @__PURE__ */ new Map();
|
|
4067
|
+
for (let listIdx = 0; listIdx < rankedLists.length; listIdx++) {
|
|
4068
|
+
const list = rankedLists[listIdx];
|
|
4069
|
+
const weight = weights[listIdx] ?? 1;
|
|
4070
|
+
for (let rank = 0; rank < list.length; rank++) {
|
|
4071
|
+
const item = list[rank];
|
|
4072
|
+
const rrfScore = weight / (k + rank + 1);
|
|
4073
|
+
const existing = fusedScores.get(item.id);
|
|
4074
|
+
if (existing) {
|
|
4075
|
+
existing.score += rrfScore;
|
|
4076
|
+
} else {
|
|
4077
|
+
fusedScores.set(item.id, {
|
|
4078
|
+
score: rrfScore,
|
|
4079
|
+
item: { ...item }
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
const results = Array.from(fusedScores.values()).map(({ score, item }) => ({
|
|
4085
|
+
...item,
|
|
4086
|
+
score
|
|
4087
|
+
}));
|
|
4088
|
+
results.sort((a, b) => b.score - a.score);
|
|
4089
|
+
if (options?.minScore != null) {
|
|
4090
|
+
const min = options.minScore;
|
|
4091
|
+
return results.filter((r) => r.score >= min).slice(0, topK);
|
|
4092
|
+
}
|
|
4093
|
+
return results.slice(0, topK);
|
|
4094
|
+
}
|
|
4095
|
+
|
|
4096
|
+
// src/knowledge/knowledge-base.ts
|
|
3882
4097
|
var KnowledgeBase = class {
|
|
3883
4098
|
name;
|
|
3884
4099
|
collection;
|
|
3885
4100
|
store;
|
|
3886
4101
|
initialized = false;
|
|
4102
|
+
bm25;
|
|
4103
|
+
defaultSearchMode;
|
|
4104
|
+
hybridConfig;
|
|
3887
4105
|
constructor(config) {
|
|
3888
4106
|
this.name = config.name;
|
|
3889
4107
|
this.store = config.vectorStore;
|
|
3890
4108
|
this.collection = config.collection ?? config.name.toLowerCase().replace(/\s+/g, "_");
|
|
4109
|
+
this.defaultSearchMode = config.searchMode ?? "vector";
|
|
4110
|
+
this.hybridConfig = {
|
|
4111
|
+
vectorWeight: config.hybridConfig?.vectorWeight ?? 1,
|
|
4112
|
+
keywordWeight: config.hybridConfig?.keywordWeight ?? 1,
|
|
4113
|
+
rrfK: config.hybridConfig?.rrfK ?? 60
|
|
4114
|
+
};
|
|
4115
|
+
this.bm25 = new BM25Index();
|
|
3891
4116
|
}
|
|
3892
4117
|
async initialize() {
|
|
3893
4118
|
if (this.initialized) return;
|
|
@@ -3897,14 +4122,27 @@ var KnowledgeBase = class {
|
|
|
3897
4122
|
async add(doc) {
|
|
3898
4123
|
await this.ensureInit();
|
|
3899
4124
|
await this.store.upsert(this.collection, doc);
|
|
4125
|
+
this.bm25.add({ id: doc.id, content: doc.content, metadata: doc.metadata });
|
|
3900
4126
|
}
|
|
3901
4127
|
async addDocuments(docs) {
|
|
3902
4128
|
await this.ensureInit();
|
|
3903
4129
|
await this.store.upsertBatch(this.collection, docs);
|
|
4130
|
+
this.bm25.addBatch(
|
|
4131
|
+
docs.map((d) => ({ id: d.id, content: d.content, metadata: d.metadata }))
|
|
4132
|
+
);
|
|
3904
4133
|
}
|
|
3905
4134
|
async search(query, options) {
|
|
3906
4135
|
await this.ensureInit();
|
|
3907
|
-
|
|
4136
|
+
const mode = options?.searchMode ?? this.defaultSearchMode;
|
|
4137
|
+
switch (mode) {
|
|
4138
|
+
case "keyword":
|
|
4139
|
+
return this.keywordSearch(query, options);
|
|
4140
|
+
case "hybrid":
|
|
4141
|
+
return this.hybridSearch(query, options);
|
|
4142
|
+
case "vector":
|
|
4143
|
+
default:
|
|
4144
|
+
return this.store.search(this.collection, query, options);
|
|
4145
|
+
}
|
|
3908
4146
|
}
|
|
3909
4147
|
async get(id) {
|
|
3910
4148
|
await this.ensureInit();
|
|
@@ -3913,9 +4151,11 @@ var KnowledgeBase = class {
|
|
|
3913
4151
|
async delete(id) {
|
|
3914
4152
|
await this.ensureInit();
|
|
3915
4153
|
await this.store.delete(this.collection, id);
|
|
4154
|
+
this.bm25.remove(id);
|
|
3916
4155
|
}
|
|
3917
4156
|
async clear() {
|
|
3918
4157
|
await this.store.dropCollection(this.collection);
|
|
4158
|
+
this.bm25.clear();
|
|
3919
4159
|
}
|
|
3920
4160
|
async close() {
|
|
3921
4161
|
await this.store.close();
|
|
@@ -3928,6 +4168,7 @@ var KnowledgeBase = class {
|
|
|
3928
4168
|
const topK = config.topK ?? 5;
|
|
3929
4169
|
const minScore = config.minScore;
|
|
3930
4170
|
const filter = config.filter;
|
|
4171
|
+
const searchMode = config.searchMode;
|
|
3931
4172
|
const toolName = config.toolName ?? `search_${this.collection}`;
|
|
3932
4173
|
const description = config.description ?? `Search the "${this.name}" knowledge base for relevant information. Use this before answering questions related to ${this.name}.`;
|
|
3933
4174
|
const formatResults = config.formatResults ?? defaultFormatResults;
|
|
@@ -3942,7 +4183,8 @@ var KnowledgeBase = class {
|
|
|
3942
4183
|
const results = await kb.search(args.query, {
|
|
3943
4184
|
topK,
|
|
3944
4185
|
minScore,
|
|
3945
|
-
filter
|
|
4186
|
+
filter,
|
|
4187
|
+
searchMode
|
|
3946
4188
|
});
|
|
3947
4189
|
if (results.length === 0) {
|
|
3948
4190
|
return "No relevant documents found in the knowledge base.";
|
|
@@ -3951,6 +4193,60 @@ var KnowledgeBase = class {
|
|
|
3951
4193
|
}
|
|
3952
4194
|
};
|
|
3953
4195
|
}
|
|
4196
|
+
keywordSearch(query, options) {
|
|
4197
|
+
const results = this.bm25.search(query, {
|
|
4198
|
+
topK: options?.topK ?? 10,
|
|
4199
|
+
filter: options?.filter
|
|
4200
|
+
});
|
|
4201
|
+
return results.map((r) => ({
|
|
4202
|
+
id: r.id,
|
|
4203
|
+
content: r.content,
|
|
4204
|
+
score: r.score,
|
|
4205
|
+
metadata: r.metadata
|
|
4206
|
+
}));
|
|
4207
|
+
}
|
|
4208
|
+
async hybridSearch(query, options) {
|
|
4209
|
+
const topK = options?.topK ?? 10;
|
|
4210
|
+
const fetchK = topK * 2;
|
|
4211
|
+
const [vectorResults, keywordResults] = await Promise.all([
|
|
4212
|
+
this.store.search(this.collection, query, {
|
|
4213
|
+
...options,
|
|
4214
|
+
topK: fetchK
|
|
4215
|
+
}),
|
|
4216
|
+
Promise.resolve(
|
|
4217
|
+
this.bm25.search(query, {
|
|
4218
|
+
topK: fetchK,
|
|
4219
|
+
filter: options?.filter
|
|
4220
|
+
})
|
|
4221
|
+
)
|
|
4222
|
+
]);
|
|
4223
|
+
const vectorRanked = vectorResults.map((r) => ({
|
|
4224
|
+
id: r.id,
|
|
4225
|
+
content: r.content,
|
|
4226
|
+
score: r.score,
|
|
4227
|
+
metadata: r.metadata
|
|
4228
|
+
}));
|
|
4229
|
+
const keywordRanked = keywordResults.map((r) => ({
|
|
4230
|
+
id: r.id,
|
|
4231
|
+
content: r.content,
|
|
4232
|
+
score: r.score,
|
|
4233
|
+
metadata: r.metadata
|
|
4234
|
+
}));
|
|
4235
|
+
const fused = reciprocalRankFusion(
|
|
4236
|
+
[vectorRanked, keywordRanked],
|
|
4237
|
+
{
|
|
4238
|
+
k: this.hybridConfig.rrfK,
|
|
4239
|
+
topK,
|
|
4240
|
+
weights: [this.hybridConfig.vectorWeight, this.hybridConfig.keywordWeight]
|
|
4241
|
+
}
|
|
4242
|
+
);
|
|
4243
|
+
return fused.map((r) => ({
|
|
4244
|
+
id: r.id,
|
|
4245
|
+
content: r.content,
|
|
4246
|
+
score: r.score,
|
|
4247
|
+
metadata: r.metadata
|
|
4248
|
+
}));
|
|
4249
|
+
}
|
|
3954
4250
|
async ensureInit() {
|
|
3955
4251
|
if (!this.initialized) await this.initialize();
|
|
3956
4252
|
}
|
|
@@ -3968,61 +4264,105 @@ ${lines.join("\n\n")}`;
|
|
|
3968
4264
|
}
|
|
3969
4265
|
|
|
3970
4266
|
// src/memory/memory.ts
|
|
3971
|
-
var SHORT_TERM_NS = "memory:short";
|
|
3972
4267
|
var LONG_TERM_NS = "memory:long";
|
|
4268
|
+
var SUMMARIZE_PROMPT = `Summarize the following conversation chunk in 2-3 concise sentences. Focus on key topics discussed, decisions made, and important information shared. Do not include greetings or filler.
|
|
4269
|
+
|
|
4270
|
+
Conversation:
|
|
4271
|
+
{conversation}
|
|
4272
|
+
|
|
4273
|
+
Summary:`;
|
|
3973
4274
|
var Memory = class {
|
|
3974
4275
|
storage;
|
|
3975
|
-
|
|
3976
|
-
|
|
4276
|
+
model;
|
|
4277
|
+
maxSummaries;
|
|
4278
|
+
initPromise = null;
|
|
3977
4279
|
constructor(config) {
|
|
3978
4280
|
this.storage = config?.storage ?? new InMemoryStorage();
|
|
3979
|
-
this.
|
|
3980
|
-
this.
|
|
3981
|
-
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
4281
|
+
this.model = config?.model;
|
|
4282
|
+
this.maxSummaries = config?.maxSummaries ?? 20;
|
|
4283
|
+
}
|
|
4284
|
+
ensureInitialized() {
|
|
4285
|
+
if (!this.initPromise) {
|
|
4286
|
+
this.initPromise = (async () => {
|
|
4287
|
+
if (typeof this.storage.initialize === "function") {
|
|
4288
|
+
await this.storage.initialize();
|
|
4289
|
+
}
|
|
4290
|
+
})();
|
|
4291
|
+
}
|
|
4292
|
+
return this.initPromise;
|
|
4293
|
+
}
|
|
4294
|
+
/**
|
|
4295
|
+
* Summarize overflow messages and store the summary.
|
|
4296
|
+
* Uses the configured LLM model; falls back to basic concatenation if no model.
|
|
4297
|
+
*/
|
|
4298
|
+
async summarize(sessionId, messages, fallbackModel) {
|
|
4299
|
+
await this.ensureInitialized();
|
|
4300
|
+
const textParts = messages.filter((m) => m.content).map((m) => `${m.role}: ${getTextContent(m.content)}`);
|
|
4301
|
+
if (textParts.length === 0) return;
|
|
4302
|
+
const model = this.model ?? fallbackModel;
|
|
4303
|
+
let summary;
|
|
4304
|
+
if (model) {
|
|
4305
|
+
try {
|
|
4306
|
+
const prompt = SUMMARIZE_PROMPT.replace(
|
|
4307
|
+
"{conversation}",
|
|
4308
|
+
textParts.join("\n")
|
|
4309
|
+
);
|
|
4310
|
+
const response = await model.generate(
|
|
4311
|
+
[{ role: "user", content: prompt }],
|
|
4312
|
+
{ temperature: 0, maxTokens: 300 }
|
|
4313
|
+
);
|
|
4314
|
+
summary = typeof response.message.content === "string" ? response.message.content.trim() : textParts.join(" | ").slice(0, 500);
|
|
4315
|
+
} catch {
|
|
4316
|
+
summary = textParts.join(" | ").slice(0, 500);
|
|
4317
|
+
}
|
|
4318
|
+
} else {
|
|
4319
|
+
summary = textParts.join(" | ").slice(0, 500);
|
|
4320
|
+
}
|
|
4321
|
+
const entry = {
|
|
4322
|
+
key: `${sessionId}:${Date.now()}`,
|
|
4323
|
+
summary,
|
|
4324
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
4325
|
+
};
|
|
4326
|
+
await this.storage.set(LONG_TERM_NS, entry.key, entry);
|
|
4327
|
+
const all = await this.storage.list(LONG_TERM_NS, sessionId);
|
|
4328
|
+
if (all.length > this.maxSummaries) {
|
|
4329
|
+
const sorted = all.sort(
|
|
4330
|
+
(a, b) => new Date(a.value.createdAt).getTime() - new Date(b.value.createdAt).getTime()
|
|
3989
4331
|
);
|
|
3990
|
-
|
|
3991
|
-
|
|
4332
|
+
const toDelete = sorted.slice(0, all.length - this.maxSummaries);
|
|
4333
|
+
for (const item of toDelete) {
|
|
4334
|
+
await this.storage.delete(LONG_TERM_NS, item.key);
|
|
3992
4335
|
}
|
|
3993
4336
|
}
|
|
3994
|
-
await this.storage.set(SHORT_TERM_NS, sessionId, updated);
|
|
3995
|
-
}
|
|
3996
|
-
async getMessages(sessionId) {
|
|
3997
|
-
return await this.storage.get(SHORT_TERM_NS, sessionId) ?? [];
|
|
3998
4337
|
}
|
|
4338
|
+
/** Get all stored summaries for a session. */
|
|
3999
4339
|
async getSummaries(sessionId) {
|
|
4000
|
-
|
|
4340
|
+
await this.ensureInitialized();
|
|
4001
4341
|
const entries = await this.storage.list(
|
|
4002
4342
|
LONG_TERM_NS,
|
|
4003
4343
|
sessionId
|
|
4004
4344
|
);
|
|
4005
|
-
return entries.
|
|
4345
|
+
return entries.sort(
|
|
4346
|
+
(a, b) => new Date(a.value.createdAt).getTime() - new Date(b.value.createdAt).getTime()
|
|
4347
|
+
).map((e) => e.value.summary);
|
|
4006
4348
|
}
|
|
4349
|
+
/** Get summaries formatted as a context string for injection into the system prompt. */
|
|
4007
4350
|
async getContextString(sessionId) {
|
|
4008
4351
|
const summaries = await this.getSummaries(sessionId);
|
|
4009
4352
|
if (summaries.length === 0) return "";
|
|
4010
|
-
return `Previous context:
|
|
4353
|
+
return `Previous conversation context:
|
|
4011
4354
|
${summaries.join("\n")}`;
|
|
4012
4355
|
}
|
|
4013
|
-
|
|
4014
|
-
const textParts = messages.filter((m) => m.content).map((m) => `${m.role}: ${getTextContent(m.content)}`);
|
|
4015
|
-
if (textParts.length === 0) return;
|
|
4016
|
-
const summary = textParts.join(" | ").slice(0, 500);
|
|
4017
|
-
const entry = {
|
|
4018
|
-
key: `${sessionId}:${Date.now()}`,
|
|
4019
|
-
summary,
|
|
4020
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
4021
|
-
};
|
|
4022
|
-
await this.storage.set(LONG_TERM_NS, entry.key, entry);
|
|
4023
|
-
}
|
|
4356
|
+
/** Clear all summaries for a session. */
|
|
4024
4357
|
async clear(sessionId) {
|
|
4025
|
-
await this.
|
|
4358
|
+
await this.ensureInitialized();
|
|
4359
|
+
const entries = await this.storage.list(
|
|
4360
|
+
LONG_TERM_NS,
|
|
4361
|
+
sessionId
|
|
4362
|
+
);
|
|
4363
|
+
for (const entry of entries) {
|
|
4364
|
+
await this.storage.delete(LONG_TERM_NS, entry.key);
|
|
4365
|
+
}
|
|
4026
4366
|
}
|
|
4027
4367
|
};
|
|
4028
4368
|
|
|
@@ -4556,6 +4896,733 @@ var A2ARemoteAgent = class {
|
|
|
4556
4896
|
}
|
|
4557
4897
|
};
|
|
4558
4898
|
|
|
4899
|
+
// src/voice/voice-agent.ts
|
|
4900
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
4901
|
+
import { v4 as uuidv46 } from "uuid";
|
|
4902
|
+
var VoiceSessionImpl = class extends EventEmitter2 {
|
|
4903
|
+
connection;
|
|
4904
|
+
constructor(connection) {
|
|
4905
|
+
super();
|
|
4906
|
+
this.connection = connection;
|
|
4907
|
+
}
|
|
4908
|
+
sendAudio(data) {
|
|
4909
|
+
this.connection.sendAudio(data);
|
|
4910
|
+
}
|
|
4911
|
+
sendText(text) {
|
|
4912
|
+
this.connection.sendText(text);
|
|
4913
|
+
}
|
|
4914
|
+
interrupt() {
|
|
4915
|
+
this.connection.interrupt();
|
|
4916
|
+
}
|
|
4917
|
+
async close() {
|
|
4918
|
+
await this.connection.close();
|
|
4919
|
+
}
|
|
4920
|
+
on(event, handler) {
|
|
4921
|
+
return super.on(event, handler);
|
|
4922
|
+
}
|
|
4923
|
+
off(event, handler) {
|
|
4924
|
+
return super.off(event, handler);
|
|
4925
|
+
}
|
|
4926
|
+
};
|
|
4927
|
+
var VoiceAgent = class {
|
|
4928
|
+
name;
|
|
4929
|
+
config;
|
|
4930
|
+
eventBus;
|
|
4931
|
+
logger;
|
|
4932
|
+
toolExecutor;
|
|
4933
|
+
constructor(config) {
|
|
4934
|
+
this.name = config.name;
|
|
4935
|
+
this.config = config;
|
|
4936
|
+
this.eventBus = config.eventBus ?? new EventBus();
|
|
4937
|
+
this.logger = new Logger({
|
|
4938
|
+
level: config.logLevel ?? "silent",
|
|
4939
|
+
prefix: config.name
|
|
4940
|
+
});
|
|
4941
|
+
this.toolExecutor = config.tools && config.tools.length > 0 ? new ToolExecutor(config.tools) : null;
|
|
4942
|
+
}
|
|
4943
|
+
async connect(opts) {
|
|
4944
|
+
const toolDefs = this.toolExecutor?.getToolDefinitions() ?? [];
|
|
4945
|
+
const sessionId = opts?.sessionId ?? this.config.sessionId ?? `voice_${uuidv46()}`;
|
|
4946
|
+
const userId = opts?.userId ?? this.config.userId;
|
|
4947
|
+
let instructions = this.config.instructions ?? "";
|
|
4948
|
+
if (this.config.userMemory && userId) {
|
|
4949
|
+
const userContext = await this.config.userMemory.getContextString(userId);
|
|
4950
|
+
if (userContext) {
|
|
4951
|
+
instructions = instructions ? `${instructions}
|
|
4952
|
+
|
|
4953
|
+
${userContext}` : userContext;
|
|
4954
|
+
const facts = await this.config.userMemory.getFacts(userId);
|
|
4955
|
+
this.logger.info(
|
|
4956
|
+
`Loaded ${facts.length} user facts for "${userId}"`
|
|
4957
|
+
);
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
const sessionConfig = {
|
|
4961
|
+
instructions,
|
|
4962
|
+
voice: this.config.voice,
|
|
4963
|
+
tools: toolDefs,
|
|
4964
|
+
inputAudioFormat: this.config.inputAudioFormat,
|
|
4965
|
+
outputAudioFormat: this.config.outputAudioFormat,
|
|
4966
|
+
turnDetection: this.config.turnDetection,
|
|
4967
|
+
temperature: this.config.temperature,
|
|
4968
|
+
maxResponseOutputTokens: this.config.maxResponseOutputTokens,
|
|
4969
|
+
apiKey: opts?.apiKey
|
|
4970
|
+
};
|
|
4971
|
+
this.logger.info("Connecting to realtime provider...");
|
|
4972
|
+
const connection = await this.config.provider.connect(sessionConfig);
|
|
4973
|
+
const session = new VoiceSessionImpl(connection);
|
|
4974
|
+
const ctx = new RunContext({
|
|
4975
|
+
sessionId,
|
|
4976
|
+
userId,
|
|
4977
|
+
eventBus: this.eventBus
|
|
4978
|
+
});
|
|
4979
|
+
const transcripts = [];
|
|
4980
|
+
let persisted = false;
|
|
4981
|
+
this.wireEvents(connection, session, ctx, transcripts);
|
|
4982
|
+
const onSessionEnd = async () => {
|
|
4983
|
+
if (persisted) return;
|
|
4984
|
+
persisted = true;
|
|
4985
|
+
await this.persistSession(sessionId, userId, transcripts);
|
|
4986
|
+
};
|
|
4987
|
+
session.on("disconnected", () => {
|
|
4988
|
+
onSessionEnd().catch(
|
|
4989
|
+
(e) => this.logger.warn(`Session persist failed: ${e}`)
|
|
4990
|
+
);
|
|
4991
|
+
});
|
|
4992
|
+
const originalClose = session.close.bind(session);
|
|
4993
|
+
session.close = async () => {
|
|
4994
|
+
await originalClose();
|
|
4995
|
+
await onSessionEnd();
|
|
4996
|
+
};
|
|
4997
|
+
this.eventBus.emit("voice.connected", { agentName: this.name });
|
|
4998
|
+
this.logger.info(
|
|
4999
|
+
`Voice session connected (session=${sessionId}, user=${userId ?? "anonymous"})`
|
|
5000
|
+
);
|
|
5001
|
+
return session;
|
|
5002
|
+
}
|
|
5003
|
+
/**
|
|
5004
|
+
* Consolidate transcript deltas into full messages.
|
|
5005
|
+
* Voice transcripts arrive as many small chunks (one per word/syllable).
|
|
5006
|
+
* Consecutive entries with the same role are merged into a single message.
|
|
5007
|
+
*/
|
|
5008
|
+
consolidateTranscripts(transcripts) {
|
|
5009
|
+
const consolidated = [];
|
|
5010
|
+
let current = null;
|
|
5011
|
+
for (const t of transcripts) {
|
|
5012
|
+
if (current && current.role === t.role) {
|
|
5013
|
+
current.content += t.text;
|
|
5014
|
+
} else {
|
|
5015
|
+
if (current && current.content.trim()) {
|
|
5016
|
+
consolidated.push(current);
|
|
5017
|
+
}
|
|
5018
|
+
current = { role: t.role, content: t.text };
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
if (current && current.content.trim()) {
|
|
5022
|
+
consolidated.push(current);
|
|
5023
|
+
}
|
|
5024
|
+
return consolidated;
|
|
5025
|
+
}
|
|
5026
|
+
async persistSession(_sessionId, userId, transcripts) {
|
|
5027
|
+
if (transcripts.length === 0) return;
|
|
5028
|
+
const messages = this.consolidateTranscripts(transcripts);
|
|
5029
|
+
this.logger.info(
|
|
5030
|
+
`Consolidated ${transcripts.length} transcript deltas into ${messages.length} messages`
|
|
5031
|
+
);
|
|
5032
|
+
for (const m of messages) {
|
|
5033
|
+
this.logger.info(` [${m.role}] ${m.content.substring(0, 100)}`);
|
|
5034
|
+
}
|
|
5035
|
+
if (this.config.userMemory && userId) {
|
|
5036
|
+
try {
|
|
5037
|
+
await this.config.userMemory.extractAndStore(
|
|
5038
|
+
userId,
|
|
5039
|
+
messages,
|
|
5040
|
+
this.config.model
|
|
5041
|
+
);
|
|
5042
|
+
const facts = await this.config.userMemory.getFacts(userId);
|
|
5043
|
+
this.logger.info(
|
|
5044
|
+
`Extracted user facts for "${userId}" \u2014 total: ${facts.length}`
|
|
5045
|
+
);
|
|
5046
|
+
} catch (e) {
|
|
5047
|
+
this.logger.warn(`UserMemory extraction failed: ${e.message ?? e}`);
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
wireEvents(connection, session, ctx, transcripts) {
|
|
5052
|
+
connection.on("audio", (data) => {
|
|
5053
|
+
session.emit("audio", data);
|
|
5054
|
+
this.eventBus.emit("voice.audio", {
|
|
5055
|
+
agentName: this.name,
|
|
5056
|
+
data: data.data
|
|
5057
|
+
});
|
|
5058
|
+
});
|
|
5059
|
+
connection.on("text", (data) => {
|
|
5060
|
+
session.emit("text", data);
|
|
5061
|
+
});
|
|
5062
|
+
connection.on("transcript", (data) => {
|
|
5063
|
+
session.emit("transcript", data);
|
|
5064
|
+
transcripts.push({ role: data.role, text: data.text });
|
|
5065
|
+
this.eventBus.emit("voice.transcript", {
|
|
5066
|
+
agentName: this.name,
|
|
5067
|
+
text: data.text,
|
|
5068
|
+
role: data.role
|
|
5069
|
+
});
|
|
5070
|
+
this.logger.info(`[${data.role}] ${data.text}`);
|
|
5071
|
+
});
|
|
5072
|
+
connection.on("tool_call", (toolCall) => {
|
|
5073
|
+
this.handleToolCall(connection, session, ctx, toolCall);
|
|
5074
|
+
});
|
|
5075
|
+
connection.on("interrupted", () => {
|
|
5076
|
+
session.emit("interrupted", {});
|
|
5077
|
+
this.logger.debug("Response interrupted by user speech");
|
|
5078
|
+
});
|
|
5079
|
+
connection.on("error", (data) => {
|
|
5080
|
+
session.emit("error", data);
|
|
5081
|
+
this.eventBus.emit("voice.error", {
|
|
5082
|
+
agentName: this.name,
|
|
5083
|
+
error: data.error
|
|
5084
|
+
});
|
|
5085
|
+
this.logger.error(`Error: ${data.error.message}`);
|
|
5086
|
+
});
|
|
5087
|
+
connection.on("disconnected", () => {
|
|
5088
|
+
session.emit("disconnected", {});
|
|
5089
|
+
this.eventBus.emit("voice.disconnected", { agentName: this.name });
|
|
5090
|
+
this.logger.info("Voice session disconnected");
|
|
5091
|
+
});
|
|
5092
|
+
}
|
|
5093
|
+
async handleToolCall(connection, session, ctx, toolCall) {
|
|
5094
|
+
if (!this.toolExecutor) {
|
|
5095
|
+
this.logger.warn(
|
|
5096
|
+
`Tool call "${toolCall.name}" received but no tools registered`
|
|
5097
|
+
);
|
|
5098
|
+
connection.sendToolResult(
|
|
5099
|
+
toolCall.id,
|
|
5100
|
+
JSON.stringify({ error: "No tools available" })
|
|
5101
|
+
);
|
|
5102
|
+
return;
|
|
5103
|
+
}
|
|
5104
|
+
let parsedArgs = {};
|
|
5105
|
+
try {
|
|
5106
|
+
parsedArgs = JSON.parse(toolCall.arguments || "{}");
|
|
5107
|
+
} catch {
|
|
5108
|
+
parsedArgs = {};
|
|
5109
|
+
}
|
|
5110
|
+
session.emit("tool_call_start", {
|
|
5111
|
+
name: toolCall.name,
|
|
5112
|
+
args: parsedArgs
|
|
5113
|
+
});
|
|
5114
|
+
this.eventBus.emit("voice.tool.call", {
|
|
5115
|
+
agentName: this.name,
|
|
5116
|
+
toolName: toolCall.name,
|
|
5117
|
+
args: parsedArgs
|
|
5118
|
+
});
|
|
5119
|
+
this.logger.info(`Tool call: ${toolCall.name}`);
|
|
5120
|
+
try {
|
|
5121
|
+
const results = await this.toolExecutor.executeAll(
|
|
5122
|
+
[{ id: toolCall.id, name: toolCall.name, arguments: parsedArgs }],
|
|
5123
|
+
ctx
|
|
5124
|
+
);
|
|
5125
|
+
const result = results[0];
|
|
5126
|
+
const resultContent = typeof result.result === "string" ? result.result : result.result.content;
|
|
5127
|
+
connection.sendToolResult(toolCall.id, resultContent);
|
|
5128
|
+
session.emit("tool_result", {
|
|
5129
|
+
name: toolCall.name,
|
|
5130
|
+
result: resultContent
|
|
5131
|
+
});
|
|
5132
|
+
this.eventBus.emit("voice.tool.result", {
|
|
5133
|
+
agentName: this.name,
|
|
5134
|
+
toolName: toolCall.name,
|
|
5135
|
+
result: resultContent
|
|
5136
|
+
});
|
|
5137
|
+
this.logger.info(
|
|
5138
|
+
`Tool result: ${toolCall.name} -> ${resultContent.substring(0, 100)}`
|
|
5139
|
+
);
|
|
5140
|
+
} catch (error) {
|
|
5141
|
+
const errMsg = error?.message ?? "Tool execution failed";
|
|
5142
|
+
connection.sendToolResult(
|
|
5143
|
+
toolCall.id,
|
|
5144
|
+
JSON.stringify({ error: errMsg })
|
|
5145
|
+
);
|
|
5146
|
+
this.logger.error(`Tool error: ${toolCall.name} -> ${errMsg}`);
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
};
|
|
5150
|
+
|
|
5151
|
+
// src/voice/providers/openai-realtime.ts
|
|
5152
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
5153
|
+
import { createRequire as createRequire16 } from "module";
|
|
5154
|
+
var _require16 = createRequire16(import.meta.url);
|
|
5155
|
+
function toOpenAIAudioFormat(fmt) {
|
|
5156
|
+
if (!fmt) return "pcm16";
|
|
5157
|
+
switch (fmt) {
|
|
5158
|
+
case "pcm16":
|
|
5159
|
+
return "pcm16";
|
|
5160
|
+
case "g711_ulaw":
|
|
5161
|
+
return "g711_ulaw";
|
|
5162
|
+
case "g711_alaw":
|
|
5163
|
+
return "g711_alaw";
|
|
5164
|
+
default:
|
|
5165
|
+
return "pcm16";
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
var OpenAIRealtimeConnection = class extends EventEmitter3 {
|
|
5169
|
+
ws;
|
|
5170
|
+
closed = false;
|
|
5171
|
+
constructor(ws) {
|
|
5172
|
+
super();
|
|
5173
|
+
this.ws = ws;
|
|
5174
|
+
}
|
|
5175
|
+
sendAudio(data) {
|
|
5176
|
+
if (this.closed) return;
|
|
5177
|
+
this.send({
|
|
5178
|
+
type: "input_audio_buffer.append",
|
|
5179
|
+
audio: data.toString("base64")
|
|
5180
|
+
});
|
|
5181
|
+
}
|
|
5182
|
+
sendText(text) {
|
|
5183
|
+
if (this.closed) return;
|
|
5184
|
+
this.send({
|
|
5185
|
+
type: "conversation.item.create",
|
|
5186
|
+
item: {
|
|
5187
|
+
type: "message",
|
|
5188
|
+
role: "user",
|
|
5189
|
+
content: [{ type: "input_text", text }]
|
|
5190
|
+
}
|
|
5191
|
+
});
|
|
5192
|
+
this.send({ type: "response.create" });
|
|
5193
|
+
}
|
|
5194
|
+
sendToolResult(callId, result) {
|
|
5195
|
+
if (this.closed) return;
|
|
5196
|
+
this.send({
|
|
5197
|
+
type: "conversation.item.create",
|
|
5198
|
+
item: {
|
|
5199
|
+
type: "function_call_output",
|
|
5200
|
+
call_id: callId,
|
|
5201
|
+
output: result
|
|
5202
|
+
}
|
|
5203
|
+
});
|
|
5204
|
+
this.send({ type: "response.create" });
|
|
5205
|
+
}
|
|
5206
|
+
interrupt() {
|
|
5207
|
+
if (this.closed) return;
|
|
5208
|
+
this.send({ type: "response.cancel" });
|
|
5209
|
+
}
|
|
5210
|
+
async close() {
|
|
5211
|
+
if (this.closed) return;
|
|
5212
|
+
this.closed = true;
|
|
5213
|
+
try {
|
|
5214
|
+
this.ws.close();
|
|
5215
|
+
} catch {
|
|
5216
|
+
}
|
|
5217
|
+
this.emit("disconnected", {});
|
|
5218
|
+
}
|
|
5219
|
+
on(event, handler) {
|
|
5220
|
+
return super.on(event, handler);
|
|
5221
|
+
}
|
|
5222
|
+
off(event, handler) {
|
|
5223
|
+
return super.off(event, handler);
|
|
5224
|
+
}
|
|
5225
|
+
send(event) {
|
|
5226
|
+
if (this.ws.readyState === 1) {
|
|
5227
|
+
this.ws.send(JSON.stringify(event));
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
/** Called by the provider to set up server event handling. */
|
|
5231
|
+
_bindServerEvents() {
|
|
5232
|
+
this.ws.on("message", (raw) => {
|
|
5233
|
+
try {
|
|
5234
|
+
const data = JSON.parse(typeof raw === "string" ? raw : raw.toString());
|
|
5235
|
+
this.handleServerEvent(data);
|
|
5236
|
+
} catch {
|
|
5237
|
+
}
|
|
5238
|
+
});
|
|
5239
|
+
this.ws.on("error", (err) => {
|
|
5240
|
+
this.emit("error", { error: err });
|
|
5241
|
+
});
|
|
5242
|
+
this.ws.on("close", () => {
|
|
5243
|
+
if (!this.closed) {
|
|
5244
|
+
this.closed = true;
|
|
5245
|
+
this.emit("disconnected", {});
|
|
5246
|
+
}
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5249
|
+
pendingFunctionCalls = /* @__PURE__ */ new Map();
|
|
5250
|
+
handleServerEvent(event) {
|
|
5251
|
+
switch (event.type) {
|
|
5252
|
+
case "session.created":
|
|
5253
|
+
this.emit("connected", {});
|
|
5254
|
+
break;
|
|
5255
|
+
case "response.audio.delta":
|
|
5256
|
+
if (event.delta) {
|
|
5257
|
+
this.emit("audio", {
|
|
5258
|
+
data: Buffer.from(event.delta, "base64"),
|
|
5259
|
+
mimeType: "audio/pcm"
|
|
5260
|
+
});
|
|
5261
|
+
}
|
|
5262
|
+
break;
|
|
5263
|
+
case "response.audio_transcript.delta":
|
|
5264
|
+
if (event.delta) {
|
|
5265
|
+
this.emit("transcript", { text: event.delta, role: "assistant" });
|
|
5266
|
+
}
|
|
5267
|
+
break;
|
|
5268
|
+
case "response.text.delta":
|
|
5269
|
+
if (event.delta) {
|
|
5270
|
+
this.emit("text", { text: event.delta });
|
|
5271
|
+
}
|
|
5272
|
+
break;
|
|
5273
|
+
case "input_audio_buffer.speech_started":
|
|
5274
|
+
this.emit("interrupted", {});
|
|
5275
|
+
break;
|
|
5276
|
+
case "conversation.item.input_audio_transcription.completed":
|
|
5277
|
+
if (event.transcript) {
|
|
5278
|
+
this.emit("transcript", { text: event.transcript, role: "user" });
|
|
5279
|
+
}
|
|
5280
|
+
break;
|
|
5281
|
+
case "response.function_call_arguments.delta":
|
|
5282
|
+
if (event.item_id) {
|
|
5283
|
+
const pending = this.pendingFunctionCalls.get(event.item_id);
|
|
5284
|
+
if (pending) {
|
|
5285
|
+
pending.args += event.delta ?? "";
|
|
5286
|
+
}
|
|
5287
|
+
}
|
|
5288
|
+
break;
|
|
5289
|
+
case "response.output_item.added":
|
|
5290
|
+
if (event.item?.type === "function_call") {
|
|
5291
|
+
this.pendingFunctionCalls.set(event.item.id, {
|
|
5292
|
+
name: event.item.name ?? "",
|
|
5293
|
+
args: ""
|
|
5294
|
+
});
|
|
5295
|
+
}
|
|
5296
|
+
break;
|
|
5297
|
+
case "response.output_item.done":
|
|
5298
|
+
if (event.item?.type === "function_call") {
|
|
5299
|
+
const pending = this.pendingFunctionCalls.get(event.item.id);
|
|
5300
|
+
const toolCall = {
|
|
5301
|
+
id: event.item.call_id ?? event.item.id,
|
|
5302
|
+
name: pending?.name ?? event.item.name ?? "",
|
|
5303
|
+
arguments: pending?.args ?? event.item.arguments ?? "{}"
|
|
5304
|
+
};
|
|
5305
|
+
this.pendingFunctionCalls.delete(event.item.id);
|
|
5306
|
+
this.emit("tool_call", toolCall);
|
|
5307
|
+
}
|
|
5308
|
+
break;
|
|
5309
|
+
case "error":
|
|
5310
|
+
this.emit("error", {
|
|
5311
|
+
error: new Error(event.error?.message ?? "Realtime API error")
|
|
5312
|
+
});
|
|
5313
|
+
break;
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
};
|
|
5317
|
+
var OpenAIRealtimeProvider = class {
|
|
5318
|
+
providerId = "openai-realtime";
|
|
5319
|
+
modelId;
|
|
5320
|
+
apiKey;
|
|
5321
|
+
baseURL;
|
|
5322
|
+
constructor(modelId, config) {
|
|
5323
|
+
this.modelId = modelId ?? "gpt-4o-realtime-preview";
|
|
5324
|
+
this.apiKey = config?.apiKey;
|
|
5325
|
+
this.baseURL = config?.baseURL;
|
|
5326
|
+
}
|
|
5327
|
+
async connect(config) {
|
|
5328
|
+
let WebSocket;
|
|
5329
|
+
try {
|
|
5330
|
+
WebSocket = _require16("ws");
|
|
5331
|
+
} catch {
|
|
5332
|
+
throw new Error("ws package is required for OpenAIRealtimeProvider. Install it: npm install ws");
|
|
5333
|
+
}
|
|
5334
|
+
const key = config.apiKey ?? this.apiKey ?? process.env.OPENAI_API_KEY;
|
|
5335
|
+
if (!key) {
|
|
5336
|
+
throw new Error(
|
|
5337
|
+
"No OpenAI API key provided for realtime connection. Set OPENAI_API_KEY env var or pass apiKey."
|
|
5338
|
+
);
|
|
5339
|
+
}
|
|
5340
|
+
const base = this.baseURL ?? "wss://api.openai.com";
|
|
5341
|
+
const url = `${base}/v1/realtime?model=${encodeURIComponent(this.modelId)}`;
|
|
5342
|
+
const ws = new WebSocket(url, {
|
|
5343
|
+
headers: {
|
|
5344
|
+
Authorization: `Bearer ${key}`,
|
|
5345
|
+
"OpenAI-Beta": "realtime=v1"
|
|
5346
|
+
}
|
|
5347
|
+
});
|
|
5348
|
+
const connection = new OpenAIRealtimeConnection(ws);
|
|
5349
|
+
return new Promise((resolve, reject) => {
|
|
5350
|
+
const TIMEOUT_MS = 3e4;
|
|
5351
|
+
const timeout = setTimeout(() => {
|
|
5352
|
+
reject(new Error(`OpenAI Realtime connection timed out after ${TIMEOUT_MS / 1e3}s`));
|
|
5353
|
+
try {
|
|
5354
|
+
ws.close();
|
|
5355
|
+
} catch {
|
|
5356
|
+
}
|
|
5357
|
+
}, TIMEOUT_MS);
|
|
5358
|
+
ws.on("open", () => {
|
|
5359
|
+
clearTimeout(timeout);
|
|
5360
|
+
connection._bindServerEvents();
|
|
5361
|
+
const sessionUpdate = {
|
|
5362
|
+
type: "session.update",
|
|
5363
|
+
session: this.buildSessionPayload(config)
|
|
5364
|
+
};
|
|
5365
|
+
ws.send(JSON.stringify(sessionUpdate));
|
|
5366
|
+
resolve(connection);
|
|
5367
|
+
});
|
|
5368
|
+
ws.on("error", (err) => {
|
|
5369
|
+
clearTimeout(timeout);
|
|
5370
|
+
reject(new Error(`OpenAI Realtime WebSocket error: ${err.message}`));
|
|
5371
|
+
});
|
|
5372
|
+
ws.on("unexpected-response", (_req, res) => {
|
|
5373
|
+
clearTimeout(timeout);
|
|
5374
|
+
let body = "";
|
|
5375
|
+
res.on("data", (chunk) => {
|
|
5376
|
+
body += chunk.toString();
|
|
5377
|
+
});
|
|
5378
|
+
res.on("end", () => {
|
|
5379
|
+
reject(new Error(`OpenAI Realtime rejected (HTTP ${res.statusCode}): ${body}`));
|
|
5380
|
+
});
|
|
5381
|
+
});
|
|
5382
|
+
});
|
|
5383
|
+
}
|
|
5384
|
+
buildSessionPayload(config) {
|
|
5385
|
+
const session = {
|
|
5386
|
+
modalities: ["text", "audio"],
|
|
5387
|
+
model: this.modelId
|
|
5388
|
+
};
|
|
5389
|
+
if (config.instructions) {
|
|
5390
|
+
session.instructions = config.instructions;
|
|
5391
|
+
}
|
|
5392
|
+
if (config.voice) {
|
|
5393
|
+
session.voice = config.voice;
|
|
5394
|
+
}
|
|
5395
|
+
if (config.inputAudioFormat) {
|
|
5396
|
+
session.input_audio_format = toOpenAIAudioFormat(config.inputAudioFormat);
|
|
5397
|
+
}
|
|
5398
|
+
if (config.outputAudioFormat) {
|
|
5399
|
+
session.output_audio_format = toOpenAIAudioFormat(config.outputAudioFormat);
|
|
5400
|
+
}
|
|
5401
|
+
if (config.turnDetection !== void 0) {
|
|
5402
|
+
if (config.turnDetection === null) {
|
|
5403
|
+
session.turn_detection = null;
|
|
5404
|
+
} else {
|
|
5405
|
+
session.turn_detection = {
|
|
5406
|
+
type: config.turnDetection.type,
|
|
5407
|
+
...config.turnDetection.threshold !== void 0 && {
|
|
5408
|
+
threshold: config.turnDetection.threshold
|
|
5409
|
+
},
|
|
5410
|
+
...config.turnDetection.prefixPaddingMs !== void 0 && {
|
|
5411
|
+
prefix_padding_ms: config.turnDetection.prefixPaddingMs
|
|
5412
|
+
},
|
|
5413
|
+
...config.turnDetection.silenceDurationMs !== void 0 && {
|
|
5414
|
+
silence_duration_ms: config.turnDetection.silenceDurationMs
|
|
5415
|
+
}
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
if (config.temperature !== void 0) {
|
|
5420
|
+
session.temperature = config.temperature;
|
|
5421
|
+
}
|
|
5422
|
+
if (config.maxResponseOutputTokens !== void 0) {
|
|
5423
|
+
session.max_response_output_tokens = config.maxResponseOutputTokens;
|
|
5424
|
+
}
|
|
5425
|
+
session.input_audio_transcription = { model: "whisper-1" };
|
|
5426
|
+
if (config.tools && config.tools.length > 0) {
|
|
5427
|
+
session.tools = config.tools.map((t) => ({
|
|
5428
|
+
type: "function",
|
|
5429
|
+
name: t.name,
|
|
5430
|
+
description: t.description,
|
|
5431
|
+
parameters: t.parameters
|
|
5432
|
+
}));
|
|
5433
|
+
}
|
|
5434
|
+
return session;
|
|
5435
|
+
}
|
|
5436
|
+
};
|
|
5437
|
+
|
|
5438
|
+
// src/voice/providers/google-live.ts
|
|
5439
|
+
import { EventEmitter as EventEmitter4 } from "events";
|
|
5440
|
+
import { createRequire as createRequire17 } from "module";
|
|
5441
|
+
var _require17 = createRequire17(import.meta.url);
|
|
5442
|
+
var GoogleLiveConnection = class extends EventEmitter4 {
|
|
5443
|
+
session;
|
|
5444
|
+
closed = false;
|
|
5445
|
+
constructor(session) {
|
|
5446
|
+
super();
|
|
5447
|
+
this.session = session;
|
|
5448
|
+
}
|
|
5449
|
+
sendAudio(data) {
|
|
5450
|
+
if (this.closed) return;
|
|
5451
|
+
this.session.sendRealtimeInput({
|
|
5452
|
+
audio: {
|
|
5453
|
+
data: data.toString("base64"),
|
|
5454
|
+
mimeType: "audio/pcm;rate=16000"
|
|
5455
|
+
}
|
|
5456
|
+
});
|
|
5457
|
+
}
|
|
5458
|
+
sendText(text) {
|
|
5459
|
+
if (this.closed) return;
|
|
5460
|
+
this.session.sendClientContent({
|
|
5461
|
+
turns: text,
|
|
5462
|
+
turnComplete: true
|
|
5463
|
+
});
|
|
5464
|
+
}
|
|
5465
|
+
sendToolResult(callId, result) {
|
|
5466
|
+
if (this.closed) return;
|
|
5467
|
+
let responseObj;
|
|
5468
|
+
try {
|
|
5469
|
+
responseObj = JSON.parse(result);
|
|
5470
|
+
} catch {
|
|
5471
|
+
responseObj = { result };
|
|
5472
|
+
}
|
|
5473
|
+
this.session.sendToolResponse({
|
|
5474
|
+
functionResponses: [
|
|
5475
|
+
{
|
|
5476
|
+
id: callId,
|
|
5477
|
+
name: callId,
|
|
5478
|
+
response: responseObj
|
|
5479
|
+
}
|
|
5480
|
+
]
|
|
5481
|
+
});
|
|
5482
|
+
}
|
|
5483
|
+
interrupt() {
|
|
5484
|
+
}
|
|
5485
|
+
async close() {
|
|
5486
|
+
if (this.closed) return;
|
|
5487
|
+
this.closed = true;
|
|
5488
|
+
try {
|
|
5489
|
+
this.session.close();
|
|
5490
|
+
} catch {
|
|
5491
|
+
}
|
|
5492
|
+
this.emit("disconnected", {});
|
|
5493
|
+
}
|
|
5494
|
+
on(event, handler) {
|
|
5495
|
+
return super.on(event, handler);
|
|
5496
|
+
}
|
|
5497
|
+
off(event, handler) {
|
|
5498
|
+
return super.off(event, handler);
|
|
5499
|
+
}
|
|
5500
|
+
/** Internal: handle server messages from the Live API. */
|
|
5501
|
+
_handleMessage(message) {
|
|
5502
|
+
if (message.serverContent?.interrupted) {
|
|
5503
|
+
this.emit("interrupted", {});
|
|
5504
|
+
return;
|
|
5505
|
+
}
|
|
5506
|
+
if (message.toolCall?.functionCalls) {
|
|
5507
|
+
for (const fc of message.toolCall.functionCalls) {
|
|
5508
|
+
const toolCall = {
|
|
5509
|
+
id: fc.id ?? fc.name,
|
|
5510
|
+
name: fc.name,
|
|
5511
|
+
arguments: JSON.stringify(fc.args ?? {})
|
|
5512
|
+
};
|
|
5513
|
+
this.emit("tool_call", toolCall);
|
|
5514
|
+
}
|
|
5515
|
+
return;
|
|
5516
|
+
}
|
|
5517
|
+
if (message.serverContent?.modelTurn?.parts) {
|
|
5518
|
+
for (const part of message.serverContent.modelTurn.parts) {
|
|
5519
|
+
if (part.inlineData && part.inlineData.data) {
|
|
5520
|
+
const mimeType = part.inlineData.mimeType ?? "audio/pcm";
|
|
5521
|
+
const buf = typeof part.inlineData.data === "string" ? Buffer.from(part.inlineData.data, "base64") : Buffer.from(part.inlineData.data);
|
|
5522
|
+
this.emit("audio", { data: buf, mimeType });
|
|
5523
|
+
}
|
|
5524
|
+
if (part.text) {
|
|
5525
|
+
this.emit("text", { text: part.text });
|
|
5526
|
+
this.emit("transcript", { text: part.text, role: "assistant" });
|
|
5527
|
+
}
|
|
5528
|
+
if (part.functionCall) {
|
|
5529
|
+
const toolCall = {
|
|
5530
|
+
id: part.functionCall.id ?? part.functionCall.name,
|
|
5531
|
+
name: part.functionCall.name,
|
|
5532
|
+
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
5533
|
+
};
|
|
5534
|
+
this.emit("tool_call", toolCall);
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
};
|
|
5540
|
+
var GoogleLiveProvider = class {
|
|
5541
|
+
providerId = "google-live";
|
|
5542
|
+
modelId;
|
|
5543
|
+
apiKey;
|
|
5544
|
+
constructor(modelId, config) {
|
|
5545
|
+
this.modelId = modelId ?? "gemini-2.5-flash-native-audio-preview-12-2025";
|
|
5546
|
+
this.apiKey = config?.apiKey;
|
|
5547
|
+
}
|
|
5548
|
+
async connect(config) {
|
|
5549
|
+
let GoogleGenAI;
|
|
5550
|
+
let Modality;
|
|
5551
|
+
try {
|
|
5552
|
+
const mod = _require17("@google/genai");
|
|
5553
|
+
GoogleGenAI = mod.GoogleGenAI;
|
|
5554
|
+
Modality = mod.Modality;
|
|
5555
|
+
} catch {
|
|
5556
|
+
throw new Error(
|
|
5557
|
+
"@google/genai package is required for GoogleLiveProvider. Install it: npm install @google/genai"
|
|
5558
|
+
);
|
|
5559
|
+
}
|
|
5560
|
+
const key = config.apiKey ?? this.apiKey ?? process.env.GOOGLE_API_KEY;
|
|
5561
|
+
if (!key) {
|
|
5562
|
+
throw new Error(
|
|
5563
|
+
"No Google API key provided for live connection. Set GOOGLE_API_KEY env var or pass apiKey."
|
|
5564
|
+
);
|
|
5565
|
+
}
|
|
5566
|
+
const ai = new GoogleGenAI({ apiKey: key });
|
|
5567
|
+
const liveConfig = {
|
|
5568
|
+
responseModalities: [Modality.AUDIO]
|
|
5569
|
+
};
|
|
5570
|
+
if (config.instructions) {
|
|
5571
|
+
liveConfig.systemInstruction = config.instructions;
|
|
5572
|
+
}
|
|
5573
|
+
if (config.voice) {
|
|
5574
|
+
liveConfig.speechConfig = { voiceConfig: { prebuiltVoiceConfig: { voiceName: config.voice } } };
|
|
5575
|
+
}
|
|
5576
|
+
if (config.temperature !== void 0) {
|
|
5577
|
+
liveConfig.temperature = config.temperature;
|
|
5578
|
+
}
|
|
5579
|
+
if (config.tools && config.tools.length > 0) {
|
|
5580
|
+
liveConfig.tools = [
|
|
5581
|
+
{
|
|
5582
|
+
functionDeclarations: config.tools.map((t) => ({
|
|
5583
|
+
name: t.name,
|
|
5584
|
+
description: t.description,
|
|
5585
|
+
parameters: t.parameters
|
|
5586
|
+
}))
|
|
5587
|
+
}
|
|
5588
|
+
];
|
|
5589
|
+
}
|
|
5590
|
+
const connection = new GoogleLiveConnection(null);
|
|
5591
|
+
return new Promise((resolve, reject) => {
|
|
5592
|
+
const timeout = setTimeout(() => {
|
|
5593
|
+
reject(new Error("Google Live connection timed out after 15s"));
|
|
5594
|
+
}, 15e3);
|
|
5595
|
+
ai.live.connect({
|
|
5596
|
+
model: this.modelId,
|
|
5597
|
+
config: liveConfig,
|
|
5598
|
+
callbacks: {
|
|
5599
|
+
onopen: () => {
|
|
5600
|
+
clearTimeout(timeout);
|
|
5601
|
+
connection.emit("connected", {});
|
|
5602
|
+
},
|
|
5603
|
+
onmessage: (message) => {
|
|
5604
|
+
connection._handleMessage(message);
|
|
5605
|
+
},
|
|
5606
|
+
onerror: (e) => {
|
|
5607
|
+
const err = e?.error ?? e?.message ?? e;
|
|
5608
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
5609
|
+
connection.emit("error", { error });
|
|
5610
|
+
},
|
|
5611
|
+
onclose: () => {
|
|
5612
|
+
connection.emit("disconnected", {});
|
|
5613
|
+
}
|
|
5614
|
+
}
|
|
5615
|
+
}).then((session) => {
|
|
5616
|
+
connection.session = session;
|
|
5617
|
+
resolve(connection);
|
|
5618
|
+
}).catch((err) => {
|
|
5619
|
+
clearTimeout(timeout);
|
|
5620
|
+
reject(err);
|
|
5621
|
+
});
|
|
5622
|
+
});
|
|
5623
|
+
}
|
|
5624
|
+
};
|
|
5625
|
+
|
|
4559
5626
|
// src/toolkits/base.ts
|
|
4560
5627
|
var Toolkit = class {
|
|
4561
5628
|
};
|
|
@@ -4666,8 +5733,8 @@ Snippet: ${r.snippet ?? ""}
|
|
|
4666
5733
|
|
|
4667
5734
|
// src/toolkits/gmail.ts
|
|
4668
5735
|
import { z as z5 } from "zod";
|
|
4669
|
-
import { createRequire as
|
|
4670
|
-
var
|
|
5736
|
+
import { createRequire as createRequire18 } from "module";
|
|
5737
|
+
var _require18 = createRequire18(import.meta.url);
|
|
4671
5738
|
var GmailToolkit = class extends Toolkit {
|
|
4672
5739
|
name = "gmail";
|
|
4673
5740
|
config;
|
|
@@ -4679,7 +5746,7 @@ var GmailToolkit = class extends Toolkit {
|
|
|
4679
5746
|
async getGmailClient() {
|
|
4680
5747
|
if (this.gmail) return this.gmail;
|
|
4681
5748
|
if (this.config.authClient) {
|
|
4682
|
-
const { google: google3 } =
|
|
5749
|
+
const { google: google3 } = _require18("googleapis");
|
|
4683
5750
|
this.gmail = google3.gmail({ version: "v1", auth: this.config.authClient });
|
|
4684
5751
|
return this.gmail;
|
|
4685
5752
|
}
|
|
@@ -4690,7 +5757,7 @@ var GmailToolkit = class extends Toolkit {
|
|
|
4690
5757
|
"GmailToolkit: Provide credentialsPath + tokenPath, or an authClient. Set GMAIL_CREDENTIALS_PATH and GMAIL_TOKEN_PATH env vars, or pass them in config."
|
|
4691
5758
|
);
|
|
4692
5759
|
}
|
|
4693
|
-
const { google: google2 } =
|
|
5760
|
+
const { google: google2 } = _require18("googleapis");
|
|
4694
5761
|
const fs = await import("fs");
|
|
4695
5762
|
const creds = JSON.parse(fs.readFileSync(credPath, "utf-8"));
|
|
4696
5763
|
const token = JSON.parse(fs.readFileSync(tokenPath, "utf-8"));
|
|
@@ -5221,11 +6288,13 @@ export {
|
|
|
5221
6288
|
A2ARemoteAgent,
|
|
5222
6289
|
Agent,
|
|
5223
6290
|
AnthropicProvider,
|
|
6291
|
+
BM25Index,
|
|
5224
6292
|
BaseVectorStore,
|
|
5225
6293
|
DuckDuckGoToolkit,
|
|
5226
6294
|
EventBus,
|
|
5227
6295
|
GmailToolkit,
|
|
5228
6296
|
GoogleEmbedding,
|
|
6297
|
+
GoogleLiveProvider,
|
|
5229
6298
|
GoogleProvider,
|
|
5230
6299
|
HackerNewsToolkit,
|
|
5231
6300
|
InMemoryStorage,
|
|
@@ -5241,6 +6310,7 @@ export {
|
|
|
5241
6310
|
OllamaProvider,
|
|
5242
6311
|
OpenAIEmbedding,
|
|
5243
6312
|
OpenAIProvider,
|
|
6313
|
+
OpenAIRealtimeProvider,
|
|
5244
6314
|
PgVectorStore,
|
|
5245
6315
|
PostgresStorage,
|
|
5246
6316
|
QdrantVectorStore,
|
|
@@ -5253,6 +6323,7 @@ export {
|
|
|
5253
6323
|
Toolkit,
|
|
5254
6324
|
UserMemory,
|
|
5255
6325
|
VertexAIProvider,
|
|
6326
|
+
VoiceAgent,
|
|
5256
6327
|
WebSearchToolkit,
|
|
5257
6328
|
WhatsAppToolkit,
|
|
5258
6329
|
Workflow,
|
|
@@ -5263,6 +6334,7 @@ export {
|
|
|
5263
6334
|
isMultiModal,
|
|
5264
6335
|
ollama,
|
|
5265
6336
|
openai,
|
|
6337
|
+
reciprocalRankFusion,
|
|
5266
6338
|
registry,
|
|
5267
6339
|
vertex,
|
|
5268
6340
|
withRetry
|