@radaros/core 0.3.5 → 0.3.6
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 +1407 -0
- package/dist/index.js +5269 -0
- package/package.json +6 -2
- package/src/a2a/a2a-remote-agent.ts +0 -270
- package/src/a2a/types.ts +0 -142
- package/src/agent/agent.ts +0 -417
- package/src/agent/llm-loop.ts +0 -290
- package/src/agent/run-context.ts +0 -35
- package/src/agent/types.ts +0 -89
- package/src/events/event-bus.ts +0 -45
- package/src/events/types.ts +0 -16
- package/src/guardrails/types.ts +0 -5
- package/src/hooks/types.ts +0 -6
- package/src/index.ts +0 -157
- package/src/knowledge/knowledge-base.ts +0 -146
- package/src/logger/logger.ts +0 -249
- package/src/mcp/mcp-client.ts +0 -264
- package/src/memory/memory.ts +0 -87
- package/src/memory/types.ts +0 -13
- package/src/memory/user-memory.ts +0 -211
- package/src/models/provider.ts +0 -22
- package/src/models/providers/anthropic.ts +0 -360
- package/src/models/providers/google.ts +0 -386
- package/src/models/providers/ollama.ts +0 -211
- package/src/models/providers/openai.ts +0 -345
- package/src/models/providers/vertex.ts +0 -427
- package/src/models/registry.ts +0 -107
- package/src/models/types.ts +0 -124
- package/src/session/session-manager.ts +0 -75
- package/src/session/types.ts +0 -10
- package/src/storage/driver.ts +0 -10
- package/src/storage/in-memory.ts +0 -44
- package/src/storage/mongodb.ts +0 -70
- package/src/storage/postgres.ts +0 -81
- package/src/storage/sqlite.ts +0 -81
- package/src/team/modes.ts +0 -1
- package/src/team/team.ts +0 -323
- package/src/team/types.ts +0 -26
- package/src/toolkits/base.ts +0 -15
- package/src/toolkits/duckduckgo.ts +0 -256
- package/src/toolkits/gmail.ts +0 -226
- package/src/toolkits/hackernews.ts +0 -121
- package/src/toolkits/websearch.ts +0 -158
- package/src/toolkits/whatsapp.ts +0 -209
- package/src/tools/define-tool.ts +0 -22
- package/src/tools/tool-executor.ts +0 -221
- package/src/tools/types.ts +0 -36
- package/src/utils/retry.ts +0 -56
- package/src/vector/base.ts +0 -44
- package/src/vector/embeddings/google.ts +0 -64
- package/src/vector/embeddings/openai.ts +0 -66
- package/src/vector/in-memory.ts +0 -115
- package/src/vector/mongodb.ts +0 -241
- package/src/vector/pgvector.ts +0 -169
- package/src/vector/qdrant.ts +0 -203
- package/src/vector/types.ts +0 -55
- package/src/workflow/step-runner.ts +0 -303
- package/src/workflow/types.ts +0 -55
- package/src/workflow/workflow.ts +0 -68
- package/tsconfig.json +0 -8
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { StorageDriver } from "../storage/driver.js";
|
|
2
|
-
import type { ChatMessage } from "../models/types.js";
|
|
3
|
-
import type { Session } from "./types.js";
|
|
4
|
-
|
|
5
|
-
const NAMESPACE = "sessions";
|
|
6
|
-
|
|
7
|
-
export class SessionManager {
|
|
8
|
-
constructor(private storage: StorageDriver) {}
|
|
9
|
-
|
|
10
|
-
async getOrCreate(sessionId: string, userId?: string): Promise<Session> {
|
|
11
|
-
const existing = await this.storage.get<Session>(NAMESPACE, sessionId);
|
|
12
|
-
if (existing) return existing;
|
|
13
|
-
|
|
14
|
-
const session: Session = {
|
|
15
|
-
sessionId,
|
|
16
|
-
userId,
|
|
17
|
-
messages: [],
|
|
18
|
-
state: {},
|
|
19
|
-
createdAt: new Date(),
|
|
20
|
-
updatedAt: new Date(),
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
await this.storage.set(NAMESPACE, sessionId, session);
|
|
24
|
-
return session;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async appendMessage(sessionId: string, msg: ChatMessage): Promise<void> {
|
|
28
|
-
const session = await this.getOrCreate(sessionId);
|
|
29
|
-
session.messages.push(msg);
|
|
30
|
-
session.updatedAt = new Date();
|
|
31
|
-
await this.storage.set(NAMESPACE, sessionId, session);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async appendMessages(
|
|
35
|
-
sessionId: string,
|
|
36
|
-
msgs: ChatMessage[]
|
|
37
|
-
): Promise<void> {
|
|
38
|
-
const session = await this.getOrCreate(sessionId);
|
|
39
|
-
session.messages.push(...msgs);
|
|
40
|
-
session.updatedAt = new Date();
|
|
41
|
-
await this.storage.set(NAMESPACE, sessionId, session);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async getHistory(
|
|
45
|
-
sessionId: string,
|
|
46
|
-
limit?: number
|
|
47
|
-
): Promise<ChatMessage[]> {
|
|
48
|
-
const session = await this.storage.get<Session>(NAMESPACE, sessionId);
|
|
49
|
-
if (!session) return [];
|
|
50
|
-
|
|
51
|
-
if (limit && limit > 0) {
|
|
52
|
-
return session.messages.slice(-limit);
|
|
53
|
-
}
|
|
54
|
-
return session.messages;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async updateState(
|
|
58
|
-
sessionId: string,
|
|
59
|
-
patch: Record<string, unknown>
|
|
60
|
-
): Promise<void> {
|
|
61
|
-
const session = await this.getOrCreate(sessionId);
|
|
62
|
-
Object.assign(session.state, patch);
|
|
63
|
-
session.updatedAt = new Date();
|
|
64
|
-
await this.storage.set(NAMESPACE, sessionId, session);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async getState(sessionId: string): Promise<Record<string, unknown>> {
|
|
68
|
-
const session = await this.storage.get<Session>(NAMESPACE, sessionId);
|
|
69
|
-
return session?.state ?? {};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async deleteSession(sessionId: string): Promise<void> {
|
|
73
|
-
await this.storage.delete(NAMESPACE, sessionId);
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/session/types.ts
DELETED
package/src/storage/driver.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export interface StorageDriver {
|
|
2
|
-
get<T>(namespace: string, key: string): Promise<T | null>;
|
|
3
|
-
set<T>(namespace: string, key: string, value: T): Promise<void>;
|
|
4
|
-
delete(namespace: string, key: string): Promise<void>;
|
|
5
|
-
list<T>(
|
|
6
|
-
namespace: string,
|
|
7
|
-
prefix?: string
|
|
8
|
-
): Promise<Array<{ key: string; value: T }>>;
|
|
9
|
-
close(): Promise<void>;
|
|
10
|
-
}
|
package/src/storage/in-memory.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { StorageDriver } from "./driver.js";
|
|
2
|
-
|
|
3
|
-
export class InMemoryStorage implements StorageDriver {
|
|
4
|
-
private store = new Map<string, string>();
|
|
5
|
-
|
|
6
|
-
private makeKey(namespace: string, key: string): string {
|
|
7
|
-
return `${namespace}:${key}`;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async get<T>(namespace: string, key: string): Promise<T | null> {
|
|
11
|
-
const raw = this.store.get(this.makeKey(namespace, key));
|
|
12
|
-
if (raw === undefined) return null;
|
|
13
|
-
return JSON.parse(raw) as T;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async set<T>(namespace: string, key: string, value: T): Promise<void> {
|
|
17
|
-
this.store.set(this.makeKey(namespace, key), JSON.stringify(value));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async delete(namespace: string, key: string): Promise<void> {
|
|
21
|
-
this.store.delete(this.makeKey(namespace, key));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async list<T>(
|
|
25
|
-
namespace: string,
|
|
26
|
-
prefix?: string
|
|
27
|
-
): Promise<Array<{ key: string; value: T }>> {
|
|
28
|
-
const nsPrefix = prefix ? `${namespace}:${prefix}` : `${namespace}:`;
|
|
29
|
-
const results: Array<{ key: string; value: T }> = [];
|
|
30
|
-
|
|
31
|
-
for (const [fullKey, raw] of this.store.entries()) {
|
|
32
|
-
if (fullKey.startsWith(nsPrefix)) {
|
|
33
|
-
const key = fullKey.slice(namespace.length + 1);
|
|
34
|
-
results.push({ key, value: JSON.parse(raw) as T });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return results;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async close(): Promise<void> {
|
|
42
|
-
this.store.clear();
|
|
43
|
-
}
|
|
44
|
-
}
|
package/src/storage/mongodb.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import type { StorageDriver } from "./driver.js";
|
|
3
|
-
|
|
4
|
-
const _require = createRequire(import.meta.url);
|
|
5
|
-
|
|
6
|
-
export class MongoDBStorage implements StorageDriver {
|
|
7
|
-
private client: any;
|
|
8
|
-
private db: any;
|
|
9
|
-
private collection: any;
|
|
10
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
private uri: string,
|
|
13
|
-
private dbName: string = "radaros",
|
|
14
|
-
private collectionName: string = "kv_store"
|
|
15
|
-
) {
|
|
16
|
-
try {
|
|
17
|
-
const { MongoClient } = _require("mongodb");
|
|
18
|
-
this.client = new MongoClient(uri);
|
|
19
|
-
} catch {
|
|
20
|
-
throw new Error(
|
|
21
|
-
"mongodb is required for MongoDBStorage. Install it: npm install mongodb"
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async initialize(): Promise<void> {
|
|
27
|
-
await this.client.connect();
|
|
28
|
-
this.db = this.client.db(this.dbName);
|
|
29
|
-
this.collection = this.db.collection(this.collectionName);
|
|
30
|
-
await this.collection.createIndex(
|
|
31
|
-
{ namespace: 1, key: 1 },
|
|
32
|
-
{ unique: true }
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async get<T>(namespace: string, key: string): Promise<T | null> {
|
|
37
|
-
const doc = await this.collection.findOne({ namespace, key });
|
|
38
|
-
if (!doc) return null;
|
|
39
|
-
return doc.value as T;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async set<T>(namespace: string, key: string, value: T): Promise<void> {
|
|
43
|
-
await this.collection.updateOne(
|
|
44
|
-
{ namespace, key },
|
|
45
|
-
{ $set: { value, updatedAt: new Date() } },
|
|
46
|
-
{ upsert: true }
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async delete(namespace: string, key: string): Promise<void> {
|
|
51
|
-
await this.collection.deleteOne({ namespace, key });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async list<T>(
|
|
55
|
-
namespace: string,
|
|
56
|
-
prefix?: string
|
|
57
|
-
): Promise<Array<{ key: string; value: T }>> {
|
|
58
|
-
const filter: Record<string, unknown> = { namespace };
|
|
59
|
-
if (prefix) {
|
|
60
|
-
filter.key = { $regex: `^${prefix}` };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const docs = await this.collection.find(filter).toArray();
|
|
64
|
-
return docs.map((doc: any) => ({ key: doc.key as string, value: doc.value as T }));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async close(): Promise<void> {
|
|
68
|
-
await this.client.close();
|
|
69
|
-
}
|
|
70
|
-
}
|
package/src/storage/postgres.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import type { StorageDriver } from "./driver.js";
|
|
3
|
-
|
|
4
|
-
const _require = createRequire(import.meta.url);
|
|
5
|
-
|
|
6
|
-
export class PostgresStorage implements StorageDriver {
|
|
7
|
-
private pool: any;
|
|
8
|
-
|
|
9
|
-
constructor(connectionString: string) {
|
|
10
|
-
try {
|
|
11
|
-
const { Pool } = _require("pg");
|
|
12
|
-
this.pool = new Pool({ connectionString });
|
|
13
|
-
} catch {
|
|
14
|
-
throw new Error(
|
|
15
|
-
"pg is required for PostgresStorage. Install it: npm install pg"
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async initialize(): Promise<void> {
|
|
21
|
-
await this.pool.query(`
|
|
22
|
-
CREATE TABLE IF NOT EXISTS kv_store (
|
|
23
|
-
namespace TEXT NOT NULL,
|
|
24
|
-
key TEXT NOT NULL,
|
|
25
|
-
value JSONB NOT NULL,
|
|
26
|
-
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
27
|
-
PRIMARY KEY (namespace, key)
|
|
28
|
-
)
|
|
29
|
-
`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async get<T>(namespace: string, key: string): Promise<T | null> {
|
|
33
|
-
const result = await this.pool.query(
|
|
34
|
-
"SELECT value FROM kv_store WHERE namespace = $1 AND key = $2",
|
|
35
|
-
[namespace, key]
|
|
36
|
-
);
|
|
37
|
-
if (result.rows.length === 0) return null;
|
|
38
|
-
return result.rows[0].value as T;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async set<T>(namespace: string, key: string, value: T): Promise<void> {
|
|
42
|
-
await this.pool.query(
|
|
43
|
-
`INSERT INTO kv_store (namespace, key, value, updated_at)
|
|
44
|
-
VALUES ($1, $2, $3, NOW())
|
|
45
|
-
ON CONFLICT (namespace, key)
|
|
46
|
-
DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`,
|
|
47
|
-
[namespace, key, JSON.stringify(value)]
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async delete(namespace: string, key: string): Promise<void> {
|
|
52
|
-
await this.pool.query(
|
|
53
|
-
"DELETE FROM kv_store WHERE namespace = $1 AND key = $2",
|
|
54
|
-
[namespace, key]
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async list<T>(
|
|
59
|
-
namespace: string,
|
|
60
|
-
prefix?: string
|
|
61
|
-
): Promise<Array<{ key: string; value: T }>> {
|
|
62
|
-
const result = prefix
|
|
63
|
-
? await this.pool.query(
|
|
64
|
-
"SELECT key, value FROM kv_store WHERE namespace = $1 AND key LIKE $2",
|
|
65
|
-
[namespace, `${prefix}%`]
|
|
66
|
-
)
|
|
67
|
-
: await this.pool.query(
|
|
68
|
-
"SELECT key, value FROM kv_store WHERE namespace = $1",
|
|
69
|
-
[namespace]
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return result.rows.map((row: { key: string; value: T }) => ({
|
|
73
|
-
key: row.key,
|
|
74
|
-
value: row.value,
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async close(): Promise<void> {
|
|
79
|
-
await this.pool.end();
|
|
80
|
-
}
|
|
81
|
-
}
|
package/src/storage/sqlite.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import type { StorageDriver } from "./driver.js";
|
|
3
|
-
|
|
4
|
-
const _require = createRequire(import.meta.url);
|
|
5
|
-
|
|
6
|
-
export class SqliteStorage implements StorageDriver {
|
|
7
|
-
private db: any;
|
|
8
|
-
|
|
9
|
-
constructor(dbPath: string) {
|
|
10
|
-
try {
|
|
11
|
-
const Database = _require("better-sqlite3");
|
|
12
|
-
this.db = new Database(dbPath);
|
|
13
|
-
this.db.pragma("journal_mode = WAL");
|
|
14
|
-
this.db.exec(`
|
|
15
|
-
CREATE TABLE IF NOT EXISTS kv_store (
|
|
16
|
-
namespace TEXT NOT NULL,
|
|
17
|
-
key TEXT NOT NULL,
|
|
18
|
-
value TEXT NOT NULL,
|
|
19
|
-
updated_at TEXT DEFAULT (datetime('now')),
|
|
20
|
-
PRIMARY KEY (namespace, key)
|
|
21
|
-
)
|
|
22
|
-
`);
|
|
23
|
-
} catch {
|
|
24
|
-
throw new Error(
|
|
25
|
-
"better-sqlite3 is required for SqliteStorage. Install it: npm install better-sqlite3"
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async get<T>(namespace: string, key: string): Promise<T | null> {
|
|
31
|
-
const row = this.db
|
|
32
|
-
.prepare("SELECT value FROM kv_store WHERE namespace = ? AND key = ?")
|
|
33
|
-
.get(namespace, key) as { value: string } | undefined;
|
|
34
|
-
if (!row) return null;
|
|
35
|
-
return JSON.parse(row.value) as T;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async set<T>(namespace: string, key: string, value: T): Promise<void> {
|
|
39
|
-
this.db
|
|
40
|
-
.prepare(
|
|
41
|
-
`INSERT INTO kv_store (namespace, key, value, updated_at)
|
|
42
|
-
VALUES (?, ?, ?, datetime('now'))
|
|
43
|
-
ON CONFLICT(namespace, key)
|
|
44
|
-
DO UPDATE SET value = excluded.value, updated_at = datetime('now')`
|
|
45
|
-
)
|
|
46
|
-
.run(namespace, key, JSON.stringify(value));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async delete(namespace: string, key: string): Promise<void> {
|
|
50
|
-
this.db
|
|
51
|
-
.prepare("DELETE FROM kv_store WHERE namespace = ? AND key = ?")
|
|
52
|
-
.run(namespace, key);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async list<T>(
|
|
56
|
-
namespace: string,
|
|
57
|
-
prefix?: string
|
|
58
|
-
): Promise<Array<{ key: string; value: T }>> {
|
|
59
|
-
const rows = prefix
|
|
60
|
-
? (this.db
|
|
61
|
-
.prepare(
|
|
62
|
-
"SELECT key, value FROM kv_store WHERE namespace = ? AND key LIKE ?"
|
|
63
|
-
)
|
|
64
|
-
.all(namespace, `${prefix}%`) as Array<{
|
|
65
|
-
key: string;
|
|
66
|
-
value: string;
|
|
67
|
-
}>)
|
|
68
|
-
: (this.db
|
|
69
|
-
.prepare("SELECT key, value FROM kv_store WHERE namespace = ?")
|
|
70
|
-
.all(namespace) as Array<{ key: string; value: string }>);
|
|
71
|
-
|
|
72
|
-
return rows.map((row) => ({
|
|
73
|
-
key: row.key,
|
|
74
|
-
value: JSON.parse(row.value) as T,
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async close(): Promise<void> {
|
|
79
|
-
this.db.close();
|
|
80
|
-
}
|
|
81
|
-
}
|
package/src/team/modes.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { TeamMode } from "./types.js";
|
package/src/team/team.ts
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import { v4 as uuidv4 } from "uuid";
|
|
2
|
-
import { EventBus } from "../events/event-bus.js";
|
|
3
|
-
import { RunContext } from "../agent/run-context.js";
|
|
4
|
-
import { getTextContent, type StreamChunk } from "../models/types.js";
|
|
5
|
-
import type { RunOpts, RunOutput } from "../agent/types.js";
|
|
6
|
-
import type { Agent } from "../agent/agent.js";
|
|
7
|
-
import { TeamMode, type TeamConfig } from "./types.js";
|
|
8
|
-
|
|
9
|
-
interface DelegationPlan {
|
|
10
|
-
memberId: string;
|
|
11
|
-
task: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class Team {
|
|
15
|
-
readonly name: string;
|
|
16
|
-
readonly eventBus: EventBus;
|
|
17
|
-
|
|
18
|
-
private config: TeamConfig;
|
|
19
|
-
|
|
20
|
-
constructor(config: TeamConfig) {
|
|
21
|
-
this.config = config;
|
|
22
|
-
this.name = config.name;
|
|
23
|
-
this.eventBus = config.eventBus ?? new EventBus();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async run(input: string, opts?: RunOpts): Promise<RunOutput> {
|
|
27
|
-
const ctx = new RunContext({
|
|
28
|
-
sessionId: opts?.sessionId ?? uuidv4(),
|
|
29
|
-
userId: opts?.userId,
|
|
30
|
-
metadata: opts?.metadata ?? {},
|
|
31
|
-
eventBus: this.eventBus,
|
|
32
|
-
sessionState: { ...(this.config.sessionState ?? {}) },
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
this.eventBus.emit("run.start", {
|
|
36
|
-
runId: ctx.runId,
|
|
37
|
-
agentName: this.name,
|
|
38
|
-
input,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
let output: RunOutput;
|
|
43
|
-
|
|
44
|
-
switch (this.config.mode) {
|
|
45
|
-
case TeamMode.Route:
|
|
46
|
-
output = await this.runRouteMode(input, ctx);
|
|
47
|
-
break;
|
|
48
|
-
case TeamMode.Broadcast:
|
|
49
|
-
output = await this.runBroadcastMode(input, ctx);
|
|
50
|
-
break;
|
|
51
|
-
case TeamMode.Collaborate:
|
|
52
|
-
output = await this.runCollaborateMode(input, ctx);
|
|
53
|
-
break;
|
|
54
|
-
case TeamMode.Coordinate:
|
|
55
|
-
default:
|
|
56
|
-
output = await this.runCoordinateMode(input, ctx);
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
this.eventBus.emit("run.complete", { runId: ctx.runId, output });
|
|
61
|
-
return output;
|
|
62
|
-
} catch (error) {
|
|
63
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
64
|
-
this.eventBus.emit("run.error", { runId: ctx.runId, error: err });
|
|
65
|
-
throw err;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async *stream(
|
|
70
|
-
input: string,
|
|
71
|
-
opts?: RunOpts
|
|
72
|
-
): AsyncGenerator<StreamChunk> {
|
|
73
|
-
const result = await this.run(input, opts);
|
|
74
|
-
yield { type: "text", text: result.text };
|
|
75
|
-
yield { type: "finish", finishReason: "stop", usage: result.usage };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private async runCoordinateMode(
|
|
79
|
-
input: string,
|
|
80
|
-
ctx: RunContext
|
|
81
|
-
): Promise<RunOutput> {
|
|
82
|
-
const memberDescriptions = this.buildMemberDescriptions();
|
|
83
|
-
const planPrompt = this.buildCoordinatorPrompt(
|
|
84
|
-
input,
|
|
85
|
-
memberDescriptions,
|
|
86
|
-
"coordinate"
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const planResponse = await this.config.model.generate([
|
|
90
|
-
{ role: "system", content: planPrompt },
|
|
91
|
-
{ role: "user", content: input },
|
|
92
|
-
]);
|
|
93
|
-
|
|
94
|
-
const delegations = this.parseDelegationPlan(
|
|
95
|
-
getTextContent(planResponse.message.content)
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const memberOutputs: Array<{ memberId: string; output: RunOutput }> = [];
|
|
99
|
-
|
|
100
|
-
for (const delegation of delegations) {
|
|
101
|
-
const member = this.findMember(delegation.memberId);
|
|
102
|
-
if (!member) continue;
|
|
103
|
-
|
|
104
|
-
this.eventBus.emit("team.delegate", {
|
|
105
|
-
runId: ctx.runId,
|
|
106
|
-
memberId: delegation.memberId,
|
|
107
|
-
task: delegation.task,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const output = await member.run(delegation.task, {
|
|
111
|
-
sessionId: ctx.sessionId,
|
|
112
|
-
});
|
|
113
|
-
memberOutputs.push({ memberId: delegation.memberId, output });
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const synthesisPrompt = this.buildSynthesisPrompt(
|
|
117
|
-
input,
|
|
118
|
-
memberOutputs
|
|
119
|
-
);
|
|
120
|
-
const synthesisResponse = await this.config.model.generate([
|
|
121
|
-
{ role: "user", content: synthesisPrompt },
|
|
122
|
-
]);
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
text: getTextContent(synthesisResponse.message.content),
|
|
126
|
-
toolCalls: memberOutputs.flatMap((o) => o.output.toolCalls),
|
|
127
|
-
usage: synthesisResponse.usage,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private async runRouteMode(
|
|
132
|
-
input: string,
|
|
133
|
-
ctx: RunContext
|
|
134
|
-
): Promise<RunOutput> {
|
|
135
|
-
const memberDescriptions = this.buildMemberDescriptions();
|
|
136
|
-
const routePrompt = this.buildCoordinatorPrompt(
|
|
137
|
-
input,
|
|
138
|
-
memberDescriptions,
|
|
139
|
-
"route"
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const routeResponse = await this.config.model.generate([
|
|
143
|
-
{ role: "system", content: routePrompt },
|
|
144
|
-
{ role: "user", content: input },
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
const selectedName = getTextContent(routeResponse.message.content).trim();
|
|
148
|
-
const member = this.findMember(selectedName);
|
|
149
|
-
|
|
150
|
-
if (!member) {
|
|
151
|
-
return {
|
|
152
|
-
text: `Could not route to member "${selectedName}". Available: ${this.config.members.map((m) => m.name).join(", ")}`,
|
|
153
|
-
toolCalls: [],
|
|
154
|
-
usage: routeResponse.usage,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this.eventBus.emit("team.delegate", {
|
|
159
|
-
runId: ctx.runId,
|
|
160
|
-
memberId: member.name,
|
|
161
|
-
task: input,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
return member.run(input, { sessionId: ctx.sessionId });
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private async runBroadcastMode(
|
|
168
|
-
input: string,
|
|
169
|
-
ctx: RunContext
|
|
170
|
-
): Promise<RunOutput> {
|
|
171
|
-
for (const member of this.config.members) {
|
|
172
|
-
this.eventBus.emit("team.delegate", {
|
|
173
|
-
runId: ctx.runId,
|
|
174
|
-
memberId: member.name,
|
|
175
|
-
task: input,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const outputs = await Promise.all(
|
|
180
|
-
this.config.members.map((member) =>
|
|
181
|
-
member.run(input, { sessionId: ctx.sessionId })
|
|
182
|
-
)
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const memberOutputs = this.config.members.map((member, i) => ({
|
|
186
|
-
memberId: member.name,
|
|
187
|
-
output: outputs[i],
|
|
188
|
-
}));
|
|
189
|
-
|
|
190
|
-
const synthesisPrompt = this.buildSynthesisPrompt(input, memberOutputs);
|
|
191
|
-
const synthesisResponse = await this.config.model.generate([
|
|
192
|
-
{ role: "user", content: synthesisPrompt },
|
|
193
|
-
]);
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
text: getTextContent(synthesisResponse.message.content),
|
|
197
|
-
toolCalls: outputs.flatMap((o) => o.toolCalls),
|
|
198
|
-
usage: synthesisResponse.usage,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private async runCollaborateMode(
|
|
203
|
-
input: string,
|
|
204
|
-
ctx: RunContext
|
|
205
|
-
): Promise<RunOutput> {
|
|
206
|
-
const maxRounds = this.config.maxRounds ?? 3;
|
|
207
|
-
let currentInput = input;
|
|
208
|
-
let finalOutput: RunOutput | null = null;
|
|
209
|
-
|
|
210
|
-
for (let round = 0; round < maxRounds; round++) {
|
|
211
|
-
for (const member of this.config.members) {
|
|
212
|
-
this.eventBus.emit("team.delegate", {
|
|
213
|
-
runId: ctx.runId,
|
|
214
|
-
memberId: member.name,
|
|
215
|
-
task: currentInput,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const outputs = await Promise.all(
|
|
220
|
-
this.config.members.map((member) =>
|
|
221
|
-
member.run(currentInput, { sessionId: ctx.sessionId })
|
|
222
|
-
)
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const memberOutputs = this.config.members.map((member, i) => ({
|
|
226
|
-
memberId: member.name,
|
|
227
|
-
output: outputs[i],
|
|
228
|
-
}));
|
|
229
|
-
|
|
230
|
-
const consensusPrompt = `Given the following responses to "${input}", determine if there is consensus. If yes, synthesize a final answer. If not, provide a follow-up question.\n\n${memberOutputs.map((o) => `${o.memberId}: ${o.output.text}`).join("\n\n")}\n\nRespond with either "CONSENSUS: <final answer>" or "FOLLOW_UP: <question>"`;
|
|
231
|
-
|
|
232
|
-
const consensusResponse = await this.config.model.generate([
|
|
233
|
-
{ role: "user", content: consensusPrompt },
|
|
234
|
-
]);
|
|
235
|
-
|
|
236
|
-
const responseText = getTextContent(consensusResponse.message.content);
|
|
237
|
-
|
|
238
|
-
if (responseText.startsWith("CONSENSUS:")) {
|
|
239
|
-
finalOutput = {
|
|
240
|
-
text: responseText.slice("CONSENSUS:".length).trim(),
|
|
241
|
-
toolCalls: outputs.flatMap((o) => o.toolCalls),
|
|
242
|
-
usage: consensusResponse.usage,
|
|
243
|
-
};
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
currentInput = responseText.startsWith("FOLLOW_UP:")
|
|
248
|
-
? responseText.slice("FOLLOW_UP:".length).trim()
|
|
249
|
-
: responseText;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (!finalOutput) {
|
|
253
|
-
const lastSynthesis = this.buildSynthesisPrompt(input, []);
|
|
254
|
-
const response = await this.config.model.generate([
|
|
255
|
-
{ role: "user", content: lastSynthesis },
|
|
256
|
-
]);
|
|
257
|
-
finalOutput = {
|
|
258
|
-
text: getTextContent(response.message.content),
|
|
259
|
-
toolCalls: [],
|
|
260
|
-
usage: response.usage,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return finalOutput!;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
private buildMemberDescriptions(): string {
|
|
268
|
-
return this.config.members
|
|
269
|
-
.map((member) => {
|
|
270
|
-
const desc =
|
|
271
|
-
typeof member.instructions === "function"
|
|
272
|
-
? "(dynamic instructions)"
|
|
273
|
-
: (member.instructions ?? "General-purpose agent");
|
|
274
|
-
return `- ${member.name}: ${desc}`;
|
|
275
|
-
})
|
|
276
|
-
.join("\n");
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
private buildCoordinatorPrompt(
|
|
280
|
-
input: string,
|
|
281
|
-
memberDescriptions: string,
|
|
282
|
-
mode: "coordinate" | "route"
|
|
283
|
-
): string {
|
|
284
|
-
if (mode === "route") {
|
|
285
|
-
return `You are a team coordinator. Based on the user's request, select the single most appropriate team member to handle it. Available members:\n${memberDescriptions}\n\nRespond with ONLY the member name, nothing else.`;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return `You are a team coordinator. Break down the user's request into subtasks and delegate to appropriate team members. Available members:\n${memberDescriptions}\n\nRespond with a JSON array of delegations: [{"memberId": "name", "task": "specific task"}]\n${this.config.instructions ? `\nAdditional instructions: ${this.config.instructions}` : ""}`;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
private buildSynthesisPrompt(
|
|
292
|
-
originalInput: string,
|
|
293
|
-
memberOutputs: Array<{ memberId: string; output: RunOutput }>
|
|
294
|
-
): string {
|
|
295
|
-
const outputsText = memberOutputs
|
|
296
|
-
.map((o) => `### ${o.memberId}\n${o.output.text}`)
|
|
297
|
-
.join("\n\n");
|
|
298
|
-
|
|
299
|
-
return `Original request: ${originalInput}\n\nTeam member responses:\n${outputsText}\n\nSynthesize these responses into a single coherent answer.`;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
private parseDelegationPlan(content: string): DelegationPlan[] {
|
|
303
|
-
try {
|
|
304
|
-
const jsonMatch = content.match(/\[[\s\S]*\]/);
|
|
305
|
-
if (jsonMatch) {
|
|
306
|
-
return JSON.parse(jsonMatch[0]);
|
|
307
|
-
}
|
|
308
|
-
} catch {
|
|
309
|
-
// fall through
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return this.config.members.map((m) => ({
|
|
313
|
-
memberId: m.name,
|
|
314
|
-
task: content,
|
|
315
|
-
}));
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
private findMember(name: string): Agent | undefined {
|
|
319
|
-
return this.config.members.find(
|
|
320
|
-
(m) => m.name.toLowerCase() === name.toLowerCase()
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|