@pikku/mongodb 0.12.15 → 0.12.18
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/CHANGELOG.md +106 -0
- package/dist/src/index.d.ts +3 -4
- package/dist/src/index.js +1 -1
- package/dist/src/mongodb-agent-run-service.d.ts +5 -4
- package/dist/src/mongodb-agent-run-service.js +4 -4
- package/dist/src/mongodb-agent-storage-service.d.ts +45 -0
- package/dist/src/mongodb-agent-storage-service.js +440 -0
- package/dist/src/mongodb-ai-storage-service.d.ts +5 -1
- package/dist/src/mongodb-ai-storage-service.js +28 -0
- package/dist/src/mongodb-deployment-service.d.ts +1 -1
- package/dist/src/mongodb-deployment-service.js +2 -2
- package/dist/src/mongodb-eventhub-store.d.ts +1 -1
- package/dist/src/mongodb-eventhub-store.js +1 -1
- package/dist/src/mongodb-workflow-run-service.d.ts +1 -1
- package/dist/src/mongodb-workflow-service.d.ts +3 -2
- package/dist/src/mongodb-workflow-service.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/index.ts +6 -4
- package/src/mongodb-agent-run-service.ts +22 -23
- package/src/{mongodb-ai-storage-service.ts → mongodb-agent-storage-service.ts} +96 -42
- package/src/mongodb-deployment-service.ts +3 -3
- package/src/mongodb-eventhub-store.ts +1 -1
- package/src/mongodb-services.test.ts +4 -4
- package/src/mongodb-workflow-run-service.ts +1 -1
- package/src/mongodb-workflow-service.ts +3 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,111 @@
|
|
|
1
1
|
# @pikku/mongodb
|
|
2
2
|
|
|
3
|
+
## 0.12.18
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7406bfe: Rename the agent runtime from `AI*` to `Agent*` (#596)
|
|
8
|
+
|
|
9
|
+
`AI` described the model provider, not the thing being named. Every symbol that
|
|
10
|
+
belongs to the agent runtime now says `Agent`; the symbols that genuinely wrap a
|
|
11
|
+
model provider — `AIEmbeddingService`, `AIProviderOptions`, `AIEmbedParams`,
|
|
12
|
+
`AITranscriptionParams`, `AIGenerateImageParams` and their siblings, and the
|
|
13
|
+
`@pikku/ai-vercel` / `@pikku/ai-deepinfra` / `@pikku/ai-voice` packages — keep
|
|
14
|
+
their names.
|
|
15
|
+
|
|
16
|
+
**Wiring**
|
|
17
|
+
- `pikkuAIAgent` → `pikkuAgent`, `pikkuAIScorer` → `pikkuAgentScorer`,
|
|
18
|
+
`pikkuAIJudge` → `pikkuAgentJudge`
|
|
19
|
+
- `CoreAIAgent` → `CoreAgent`, `AIAgentInput` → `AgentInput`, `AIAgentStep` →
|
|
20
|
+
`AgentStep`, `AIMessage` → `AgentMessage`, and the rest of the agent types
|
|
21
|
+
- `AIAgentRunnerService` → `AgentRunnerService`, `AIStorageService` →
|
|
22
|
+
`AgentStorageService`, `AIRunStateService` → `AgentRunStateService`
|
|
23
|
+
|
|
24
|
+
**Entry points**
|
|
25
|
+
|
|
26
|
+
`@pikku/core/agent` → `@pikku/core/agent`, `@pikku/core/agent-scorer` →
|
|
27
|
+
`@pikku/core/agent-scorer`.
|
|
28
|
+
|
|
29
|
+
**Queues**
|
|
30
|
+
|
|
31
|
+
The scorer queues are now `agent-score-fast` and `agent-score-slow`. Drain the
|
|
32
|
+
old `ai-score-fast` / `ai-score-slow` queues before deploying — jobs still
|
|
33
|
+
sitting on them when the new workers start will never be picked up.
|
|
34
|
+
|
|
35
|
+
**Scaffolds**
|
|
36
|
+
|
|
37
|
+
The agent scaffold pikku wrote for your project — `<scaffold>/agent/agent.gen.ts`
|
|
38
|
+
and its schemas file — imports `@pikku/core/ai-agent`, which no longer exists. A
|
|
39
|
+
scaffold is normally written once and then left alone, so `pikku all` would find
|
|
40
|
+
it present and leave the broken import in place. It now deletes an agent scaffold
|
|
41
|
+
importing either removed entry point and regenerates it in the same run. Anything
|
|
42
|
+
you added to that file goes with it, so move local edits out first.
|
|
43
|
+
|
|
44
|
+
**Database**
|
|
45
|
+
|
|
46
|
+
The agent tables are renamed: `ai_threads`, `ai_message`, `ai_tool_call`,
|
|
47
|
+
`ai_working_memory`, `ai_run` and `ai_run_score` become `agent_threads`,
|
|
48
|
+
`agent_message`, `agent_tool_call`, `agent_working_memory`, `agent_run` and
|
|
49
|
+
`agent_run_score`, along with their indexes and the `ai_working_memory_pk`
|
|
50
|
+
constraint. The same rename applies to the MongoDB collections.
|
|
51
|
+
|
|
52
|
+
`ensurePikkuSchema` creates tables it cannot find, so an existing database will
|
|
53
|
+
get empty `agent_*` tables and leave the old data stranded in `ai_*`. Rename
|
|
54
|
+
them before the first boot on the new version:
|
|
55
|
+
|
|
56
|
+
```sql
|
|
57
|
+
ALTER TABLE ai_threads RENAME TO agent_threads;
|
|
58
|
+
ALTER TABLE ai_message RENAME TO agent_message;
|
|
59
|
+
ALTER TABLE ai_tool_call RENAME TO agent_tool_call;
|
|
60
|
+
ALTER TABLE ai_working_memory RENAME TO agent_working_memory;
|
|
61
|
+
ALTER TABLE ai_run RENAME TO agent_run;
|
|
62
|
+
ALTER TABLE ai_run_score RENAME TO agent_run_score;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- 6794681: Publish the ecosystem surface as per-area sub-barrels under `@pikku/core/ecosystem/*`, and point generated code, the CLI, the inspector and the runtime adapters at them.
|
|
66
|
+
|
|
67
|
+
348 names that only generated code, the toolchain or a runtime adapter ever imports now have a second home on `@pikku/core/ecosystem/<area>` — one sub-barrel per area, matching how core already publishes its entrypoints, so no single barrel grows without bound and a consumer only pulls in the area it uses.
|
|
68
|
+
|
|
69
|
+
This step is additive: every name is still exported from the entrypoint it was published from before, so nothing downstream breaks. Removing them from the app-facing barrels is a later change, and needs a release carrying `./ecosystem/*` first.
|
|
70
|
+
|
|
71
|
+
- Updated dependencies [7406bfe]
|
|
72
|
+
- Updated dependencies [6794681]
|
|
73
|
+
- @pikku/core@0.12.84
|
|
74
|
+
|
|
75
|
+
## 0.12.17
|
|
76
|
+
|
|
77
|
+
### Patch Changes
|
|
78
|
+
|
|
79
|
+
- fd9d834: Stop publishing internals that only their own package or file used. The declarations stay; only the entrypoint re-export is removed, so nothing that imported a name from where it is declared is affected.
|
|
80
|
+
- Updated dependencies [063f43a]
|
|
81
|
+
- Updated dependencies [ce66bf8]
|
|
82
|
+
- Updated dependencies [d0307a8]
|
|
83
|
+
- Updated dependencies [ce66bf8]
|
|
84
|
+
- Updated dependencies [3ad2131]
|
|
85
|
+
- Updated dependencies [b930dca]
|
|
86
|
+
- Updated dependencies [b95e77d]
|
|
87
|
+
- Updated dependencies [fd9d834]
|
|
88
|
+
- Updated dependencies [8978fbd]
|
|
89
|
+
- @pikku/core@0.12.82
|
|
90
|
+
|
|
91
|
+
## 0.12.16
|
|
92
|
+
|
|
93
|
+
### Patch Changes
|
|
94
|
+
|
|
95
|
+
- e110c55: Add runtime scoring for AI agents: `pikkuAIScorer` for heuristic grades and
|
|
96
|
+
`pikkuAIJudge` for LLM-judged ones, graded off the request path on two queue
|
|
97
|
+
lanes so a slow judge cannot starve the cheap checks. Grades are sampled
|
|
98
|
+
deterministically per `(run, scorer)` and persisted to `ai_run_score`.
|
|
99
|
+
- Updated dependencies [e110c55]
|
|
100
|
+
- Updated dependencies [e110c55]
|
|
101
|
+
- Updated dependencies [e110c55]
|
|
102
|
+
- Updated dependencies [acc8077]
|
|
103
|
+
- Updated dependencies [905f737]
|
|
104
|
+
- Updated dependencies [3cc6428]
|
|
105
|
+
- Updated dependencies [c524adf]
|
|
106
|
+
- Updated dependencies [e110c55]
|
|
107
|
+
- @pikku/core@0.12.81
|
|
108
|
+
|
|
3
109
|
## 0.12.15
|
|
4
110
|
|
|
5
111
|
### Patch Changes
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,10 +4,9 @@ export { MongoDBEventHubStore } from './mongodb-eventhub-store.js';
|
|
|
4
4
|
export { MongoDBWorkflowService } from './mongodb-workflow-service.js';
|
|
5
5
|
export { MongoDBWorkflowRunService } from './mongodb-workflow-run-service.js';
|
|
6
6
|
export { MongoDBDeploymentService } from './mongodb-deployment-service.js';
|
|
7
|
-
export {
|
|
7
|
+
export { MongoDBAgentStorageService } from './mongodb-agent-storage-service.js';
|
|
8
8
|
export { MongoDBAgentRunService } from './mongodb-agent-run-service.js';
|
|
9
9
|
export { MongoDBSecretService } from './mongodb-secret-service.js';
|
|
10
|
-
export type {
|
|
11
|
-
export type {
|
|
12
|
-
export type { AgentRunService, AgentRunRow } from '@pikku/core/ai-agent';
|
|
10
|
+
export type { WorkflowRunService } from '@pikku/core/ecosystem/workflow';
|
|
11
|
+
export type { AgentRunService, AgentRunRow, } from '@pikku/core/ecosystem/agent';
|
|
13
12
|
export { MongoDBSessionStore } from './mongodb-session-store.js';
|
package/dist/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { MongoDBEventHubStore } from './mongodb-eventhub-store.js';
|
|
|
4
4
|
export { MongoDBWorkflowService } from './mongodb-workflow-service.js';
|
|
5
5
|
export { MongoDBWorkflowRunService } from './mongodb-workflow-run-service.js';
|
|
6
6
|
export { MongoDBDeploymentService } from './mongodb-deployment-service.js';
|
|
7
|
-
export {
|
|
7
|
+
export { MongoDBAgentStorageService } from './mongodb-agent-storage-service.js';
|
|
8
8
|
export { MongoDBAgentRunService } from './mongodb-agent-run-service.js';
|
|
9
9
|
export { MongoDBSecretService } from './mongodb-secret-service.js';
|
|
10
10
|
export { MongoDBSessionStore } from './mongodb-session-store.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AgentThread, AgentMessage } from '@pikku/core/agent';
|
|
2
|
+
import type { AgentRunRow, AgentRunService } from '@pikku/core/ecosystem/agent';
|
|
2
3
|
import type { Db } from 'mongodb';
|
|
3
4
|
export declare class MongoDBAgentRunService implements AgentRunService {
|
|
4
5
|
private threads;
|
|
@@ -12,9 +13,9 @@ export declare class MongoDBAgentRunService implements AgentRunService {
|
|
|
12
13
|
owners?: string[];
|
|
13
14
|
limit?: number;
|
|
14
15
|
offset?: number;
|
|
15
|
-
}): Promise<
|
|
16
|
-
getThread(threadId: string): Promise<
|
|
17
|
-
getThreadMessages(threadId: string): Promise<
|
|
16
|
+
}): Promise<AgentThread[]>;
|
|
17
|
+
getThread(threadId: string): Promise<AgentThread | null>;
|
|
18
|
+
getThreadMessages(threadId: string): Promise<AgentMessage[]>;
|
|
18
19
|
getThreadRuns(threadId: string): Promise<AgentRunRow[]>;
|
|
19
20
|
deleteThread(threadId: string): Promise<boolean>;
|
|
20
21
|
getDistinctAgentNames(): Promise<string[]>;
|
|
@@ -7,10 +7,10 @@ export class MongoDBAgentRunService {
|
|
|
7
7
|
toolCalls;
|
|
8
8
|
runs;
|
|
9
9
|
constructor(db) {
|
|
10
|
-
this.threads = db.collection('
|
|
11
|
-
this.messages = db.collection('
|
|
12
|
-
this.toolCalls = db.collection('
|
|
13
|
-
this.runs = db.collection('
|
|
10
|
+
this.threads = db.collection('agent_threads');
|
|
11
|
+
this.messages = db.collection('agent_message');
|
|
12
|
+
this.toolCalls = db.collection('agent_tool_call');
|
|
13
|
+
this.runs = db.collection('agent_run');
|
|
14
14
|
}
|
|
15
15
|
async listThreads(options) {
|
|
16
16
|
const { agentName, resourceId, owners, limit = 50, offset = 0, } = options ?? {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgentStorageService, AgentRunStateService, CreateRunInput, SaveScoreInput } from '@pikku/core/services';
|
|
2
|
+
import type { AgentThread, AgentMessage, AgentRunState } from '@pikku/core/agent';
|
|
3
|
+
import type { AgentRunScore } from '@pikku/core/agent-scorer';
|
|
4
|
+
import type { Db } from 'mongodb';
|
|
5
|
+
export declare class MongoDBAgentStorageService implements AgentStorageService, AgentRunStateService {
|
|
6
|
+
private db;
|
|
7
|
+
private initialized;
|
|
8
|
+
private threads;
|
|
9
|
+
private agentMessages;
|
|
10
|
+
private toolCalls;
|
|
11
|
+
private workingMemory;
|
|
12
|
+
private agentRuns;
|
|
13
|
+
private agentRunScores;
|
|
14
|
+
constructor(db: Db);
|
|
15
|
+
init(): Promise<void>;
|
|
16
|
+
createThread(resourceId: string, options?: {
|
|
17
|
+
threadId?: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}): Promise<AgentThread>;
|
|
21
|
+
getThread(threadId: string): Promise<AgentThread>;
|
|
22
|
+
getThreads(resourceId: string): Promise<AgentThread[]>;
|
|
23
|
+
deleteThread(threadId: string): Promise<void>;
|
|
24
|
+
getMessages(threadId: string, options?: {
|
|
25
|
+
lastN?: number;
|
|
26
|
+
cursor?: string;
|
|
27
|
+
}): Promise<AgentMessage[]>;
|
|
28
|
+
saveMessages(threadId: string, messages: AgentMessage[]): Promise<void>;
|
|
29
|
+
getWorkingMemory(id: string, scope: 'resource' | 'thread'): Promise<Record<string, unknown> | null>;
|
|
30
|
+
saveWorkingMemory(id: string, scope: 'resource' | 'thread', data: Record<string, unknown>): Promise<void>;
|
|
31
|
+
createRun(run: CreateRunInput): Promise<string>;
|
|
32
|
+
updateRun(runId: string, updates: Partial<AgentRunState>): Promise<void>;
|
|
33
|
+
getRun(runId: string): Promise<AgentRunState | null>;
|
|
34
|
+
getRunsByThread(threadId: string): Promise<AgentRunState[]>;
|
|
35
|
+
private setApprovals;
|
|
36
|
+
private mapRunRow;
|
|
37
|
+
findRunByToolCallId(toolCallId: string): Promise<{
|
|
38
|
+
run: AgentRunState;
|
|
39
|
+
approval: NonNullable<AgentRunState['pendingApprovals']>[number];
|
|
40
|
+
} | null>;
|
|
41
|
+
resolveApproval(toolCallId: string, status: 'approved' | 'denied'): Promise<boolean>;
|
|
42
|
+
saveScore(score: SaveScoreInput): Promise<void>;
|
|
43
|
+
getScores(runId: string): Promise<AgentRunScore[]>;
|
|
44
|
+
close(): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
export class MongoDBAgentStorageService {
|
|
2
|
+
db;
|
|
3
|
+
initialized = false;
|
|
4
|
+
threads;
|
|
5
|
+
agentMessages;
|
|
6
|
+
toolCalls;
|
|
7
|
+
workingMemory;
|
|
8
|
+
agentRuns;
|
|
9
|
+
agentRunScores;
|
|
10
|
+
constructor(db) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
}
|
|
13
|
+
async init() {
|
|
14
|
+
if (this.initialized)
|
|
15
|
+
return;
|
|
16
|
+
this.threads = this.db.collection('agent_threads');
|
|
17
|
+
this.agentMessages = this.db.collection('agent_message');
|
|
18
|
+
this.toolCalls = this.db.collection('agent_tool_call');
|
|
19
|
+
this.workingMemory = this.db.collection('agent_working_memory');
|
|
20
|
+
this.agentRuns = this.db.collection('agent_run');
|
|
21
|
+
this.agentRunScores =
|
|
22
|
+
this.db.collection('agent_run_score');
|
|
23
|
+
await this.threads.createIndex({ resourceId: 1 });
|
|
24
|
+
await this.agentMessages.createIndex({ threadId: 1, createdAt: 1 });
|
|
25
|
+
await this.toolCalls.createIndex({ threadId: 1 });
|
|
26
|
+
await this.toolCalls.createIndex({ messageId: 1 });
|
|
27
|
+
await this.workingMemory.createIndex({ id: 1, scope: 1 }, { unique: true });
|
|
28
|
+
await this.agentRuns.createIndex({ threadId: 1, createdAt: 1 });
|
|
29
|
+
await this.agentRunScores.createIndex({ runId: 1, createdAt: 1 });
|
|
30
|
+
this.initialized = true;
|
|
31
|
+
}
|
|
32
|
+
async createThread(resourceId, options) {
|
|
33
|
+
const id = options?.threadId ?? crypto.randomUUID();
|
|
34
|
+
const now = new Date();
|
|
35
|
+
await this.threads.insertOne({
|
|
36
|
+
_id: id,
|
|
37
|
+
resourceId,
|
|
38
|
+
title: options?.title ?? null,
|
|
39
|
+
metadata: options?.metadata ?? null,
|
|
40
|
+
createdAt: now,
|
|
41
|
+
updatedAt: now,
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
resourceId,
|
|
46
|
+
title: options?.title,
|
|
47
|
+
metadata: options?.metadata,
|
|
48
|
+
createdAt: now,
|
|
49
|
+
updatedAt: now,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async getThread(threadId) {
|
|
53
|
+
const row = await this.threads.findOne({ _id: threadId });
|
|
54
|
+
if (!row) {
|
|
55
|
+
throw new Error(`Thread not found: ${threadId}`);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
id: row._id,
|
|
59
|
+
resourceId: row.resourceId,
|
|
60
|
+
title: row.title ?? undefined,
|
|
61
|
+
metadata: row.metadata ?? undefined,
|
|
62
|
+
createdAt: new Date(row.createdAt),
|
|
63
|
+
updatedAt: new Date(row.updatedAt),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async getThreads(resourceId) {
|
|
67
|
+
const result = await this.threads
|
|
68
|
+
.find({ resourceId })
|
|
69
|
+
.sort({ updatedAt: -1 })
|
|
70
|
+
.toArray();
|
|
71
|
+
return result.map((row) => ({
|
|
72
|
+
id: row._id,
|
|
73
|
+
resourceId: row.resourceId,
|
|
74
|
+
title: row.title ?? undefined,
|
|
75
|
+
metadata: row.metadata ?? undefined,
|
|
76
|
+
createdAt: new Date(row.createdAt),
|
|
77
|
+
updatedAt: new Date(row.updatedAt),
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
async deleteThread(threadId) {
|
|
81
|
+
await this.toolCalls.deleteMany({ threadId });
|
|
82
|
+
await this.agentMessages.deleteMany({ threadId });
|
|
83
|
+
await this.agentRuns.deleteMany({ threadId });
|
|
84
|
+
await this.threads.deleteOne({ _id: threadId });
|
|
85
|
+
}
|
|
86
|
+
async getMessages(threadId, options) {
|
|
87
|
+
let filter = { threadId };
|
|
88
|
+
if (options?.cursor) {
|
|
89
|
+
const cursorRow = await this.agentMessages.findOne({
|
|
90
|
+
_id: options.cursor,
|
|
91
|
+
});
|
|
92
|
+
if (cursorRow) {
|
|
93
|
+
filter.createdAt = { $lt: cursorRow.createdAt };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
let msgResult;
|
|
97
|
+
if (options?.cursor || options?.lastN) {
|
|
98
|
+
const innerResult = await this.agentMessages
|
|
99
|
+
.find(filter)
|
|
100
|
+
.sort({ createdAt: -1 })
|
|
101
|
+
.limit(options?.lastN ?? 50)
|
|
102
|
+
.toArray();
|
|
103
|
+
innerResult.reverse();
|
|
104
|
+
msgResult = innerResult;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
msgResult = await this.agentMessages
|
|
108
|
+
.find(filter)
|
|
109
|
+
.sort({ createdAt: 1 })
|
|
110
|
+
.toArray();
|
|
111
|
+
}
|
|
112
|
+
const tcResult = await this.toolCalls
|
|
113
|
+
.find({ threadId })
|
|
114
|
+
.sort({ createdAt: 1 })
|
|
115
|
+
.toArray();
|
|
116
|
+
const tcByMessage = new Map();
|
|
117
|
+
for (const tc of tcResult) {
|
|
118
|
+
const msgId = tc.messageId;
|
|
119
|
+
if (!tcByMessage.has(msgId))
|
|
120
|
+
tcByMessage.set(msgId, []);
|
|
121
|
+
tcByMessage.get(msgId).push(tc);
|
|
122
|
+
}
|
|
123
|
+
const messages = [];
|
|
124
|
+
for (const row of msgResult) {
|
|
125
|
+
let parsedContent = row.content ?? undefined;
|
|
126
|
+
if (typeof row.content === 'string') {
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(row.content);
|
|
129
|
+
if (Array.isArray(parsed)) {
|
|
130
|
+
parsedContent = parsed;
|
|
131
|
+
}
|
|
132
|
+
else if (typeof parsed === 'string') {
|
|
133
|
+
parsedContent = parsed;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Not JSON, use raw string
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const msg = {
|
|
141
|
+
id: row._id,
|
|
142
|
+
role: row.role,
|
|
143
|
+
content: parsedContent,
|
|
144
|
+
createdAt: new Date(row.createdAt),
|
|
145
|
+
};
|
|
146
|
+
const tcs = tcByMessage.get(msg.id);
|
|
147
|
+
if (tcs?.length) {
|
|
148
|
+
msg.toolCalls = tcs.map((tc) => ({
|
|
149
|
+
id: tc._id,
|
|
150
|
+
name: tc.toolName,
|
|
151
|
+
args: tc.args,
|
|
152
|
+
}));
|
|
153
|
+
const completed = tcs.filter((tc) => tc.result != null);
|
|
154
|
+
if (completed.length) {
|
|
155
|
+
messages.push(msg);
|
|
156
|
+
messages.push({
|
|
157
|
+
id: `tool-results-${msg.id}`,
|
|
158
|
+
role: 'tool',
|
|
159
|
+
toolResults: completed.map((tc) => ({
|
|
160
|
+
id: tc._id,
|
|
161
|
+
name: tc.toolName,
|
|
162
|
+
result: tc.result,
|
|
163
|
+
})),
|
|
164
|
+
createdAt: msg.createdAt,
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
messages.push(msg);
|
|
170
|
+
}
|
|
171
|
+
return messages;
|
|
172
|
+
}
|
|
173
|
+
async saveMessages(threadId, messages) {
|
|
174
|
+
if (messages.length === 0)
|
|
175
|
+
return;
|
|
176
|
+
const toolMessages = messages.filter((m) => m.role === 'tool');
|
|
177
|
+
const nonToolMessages = messages.filter((m) => m.role !== 'tool');
|
|
178
|
+
if (nonToolMessages.length > 0) {
|
|
179
|
+
await this.agentMessages.insertMany(nonToolMessages.map((msg) => ({
|
|
180
|
+
_id: msg.id,
|
|
181
|
+
threadId,
|
|
182
|
+
role: msg.role,
|
|
183
|
+
content: msg.content != null ? JSON.stringify(msg.content) : null,
|
|
184
|
+
createdAt: msg.createdAt ?? new Date(),
|
|
185
|
+
})));
|
|
186
|
+
}
|
|
187
|
+
const toolCallDocs = nonToolMessages.flatMap((msg) => msg.toolCalls?.map((tc) => ({ ...tc, messageId: msg.id })) ?? []);
|
|
188
|
+
if (toolCallDocs.length > 0) {
|
|
189
|
+
await this.toolCalls.insertMany(toolCallDocs.map((tc) => ({
|
|
190
|
+
_id: tc.id,
|
|
191
|
+
threadId,
|
|
192
|
+
messageId: tc.messageId,
|
|
193
|
+
runId: null,
|
|
194
|
+
toolName: tc.name,
|
|
195
|
+
args: tc.args,
|
|
196
|
+
result: null,
|
|
197
|
+
approvalStatus: null,
|
|
198
|
+
approvalType: null,
|
|
199
|
+
agentRunId: null,
|
|
200
|
+
displayToolName: null,
|
|
201
|
+
displayArgs: null,
|
|
202
|
+
createdAt: new Date(),
|
|
203
|
+
})));
|
|
204
|
+
}
|
|
205
|
+
for (const toolMsg of toolMessages) {
|
|
206
|
+
if (!toolMsg.toolResults)
|
|
207
|
+
continue;
|
|
208
|
+
for (const tr of toolMsg.toolResults) {
|
|
209
|
+
await this.toolCalls.updateOne({ _id: tr.id }, { $set: { result: tr.result } });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
await this.threads.updateOne({ _id: threadId }, { $set: { updatedAt: new Date() } });
|
|
213
|
+
}
|
|
214
|
+
async getWorkingMemory(id, scope) {
|
|
215
|
+
const row = await this.workingMemory.findOne({ id, scope });
|
|
216
|
+
if (!row)
|
|
217
|
+
return null;
|
|
218
|
+
return row.data;
|
|
219
|
+
}
|
|
220
|
+
async saveWorkingMemory(id, scope, data) {
|
|
221
|
+
await this.workingMemory.updateOne({ id, scope }, {
|
|
222
|
+
$set: {
|
|
223
|
+
data,
|
|
224
|
+
updatedAt: new Date(),
|
|
225
|
+
},
|
|
226
|
+
$setOnInsert: {
|
|
227
|
+
id,
|
|
228
|
+
scope,
|
|
229
|
+
},
|
|
230
|
+
}, { upsert: true });
|
|
231
|
+
}
|
|
232
|
+
async createRun(run) {
|
|
233
|
+
const runId = crypto.randomUUID();
|
|
234
|
+
await this.agentRuns.insertOne({
|
|
235
|
+
_id: runId,
|
|
236
|
+
agentName: run.agentName,
|
|
237
|
+
threadId: run.threadId,
|
|
238
|
+
resourceId: run.resourceId,
|
|
239
|
+
status: run.status,
|
|
240
|
+
errorMessage: run.errorMessage ?? null,
|
|
241
|
+
suspendReason: run.suspendReason ?? null,
|
|
242
|
+
missingRpcs: run.missingRpcs ?? null,
|
|
243
|
+
usageInputTokens: run.usage.inputTokens,
|
|
244
|
+
usageOutputTokens: run.usage.outputTokens,
|
|
245
|
+
usageModel: run.usage.model,
|
|
246
|
+
createdAt: run.createdAt,
|
|
247
|
+
updatedAt: run.updatedAt,
|
|
248
|
+
});
|
|
249
|
+
if (run.pendingApprovals?.length) {
|
|
250
|
+
await this.setApprovals(runId, run.pendingApprovals);
|
|
251
|
+
}
|
|
252
|
+
return runId;
|
|
253
|
+
}
|
|
254
|
+
async updateRun(runId, updates) {
|
|
255
|
+
const setValues = { updatedAt: new Date() };
|
|
256
|
+
if (updates.status !== undefined) {
|
|
257
|
+
setValues.status = updates.status;
|
|
258
|
+
}
|
|
259
|
+
if (updates.errorMessage !== undefined) {
|
|
260
|
+
setValues.errorMessage = updates.errorMessage;
|
|
261
|
+
}
|
|
262
|
+
if (updates.suspendReason !== undefined) {
|
|
263
|
+
setValues.suspendReason = updates.suspendReason;
|
|
264
|
+
}
|
|
265
|
+
if (updates.missingRpcs !== undefined) {
|
|
266
|
+
setValues.missingRpcs = updates.missingRpcs;
|
|
267
|
+
}
|
|
268
|
+
if (updates.usage !== undefined) {
|
|
269
|
+
setValues.usageInputTokens = updates.usage.inputTokens;
|
|
270
|
+
setValues.usageOutputTokens = updates.usage.outputTokens;
|
|
271
|
+
setValues.usageModel = updates.usage.model;
|
|
272
|
+
}
|
|
273
|
+
await this.agentRuns.updateOne({ _id: runId }, { $set: setValues });
|
|
274
|
+
if (updates.pendingApprovals !== undefined) {
|
|
275
|
+
await this.toolCalls.updateMany({ runId, approvalStatus: { $ne: null } }, {
|
|
276
|
+
$set: {
|
|
277
|
+
approvalStatus: null,
|
|
278
|
+
runId: null,
|
|
279
|
+
approvalType: null,
|
|
280
|
+
agentRunId: null,
|
|
281
|
+
displayToolName: null,
|
|
282
|
+
displayArgs: null,
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
if (updates.pendingApprovals.length) {
|
|
286
|
+
await this.setApprovals(runId, updates.pendingApprovals);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
async getRun(runId) {
|
|
291
|
+
const row = await this.agentRuns.findOne({ _id: runId });
|
|
292
|
+
if (!row)
|
|
293
|
+
return null;
|
|
294
|
+
const approvals = await this.toolCalls
|
|
295
|
+
.find({ runId, approvalStatus: 'pending' })
|
|
296
|
+
.toArray();
|
|
297
|
+
return this.mapRunRow(row, approvals);
|
|
298
|
+
}
|
|
299
|
+
async getRunsByThread(threadId) {
|
|
300
|
+
const result = await this.agentRuns
|
|
301
|
+
.find({ threadId })
|
|
302
|
+
.sort({ createdAt: -1 })
|
|
303
|
+
.toArray();
|
|
304
|
+
const runs = [];
|
|
305
|
+
for (const row of result) {
|
|
306
|
+
const approvals = await this.toolCalls
|
|
307
|
+
.find({ runId: row._id, approvalStatus: 'pending' })
|
|
308
|
+
.toArray();
|
|
309
|
+
runs.push(this.mapRunRow(row, approvals));
|
|
310
|
+
}
|
|
311
|
+
return runs;
|
|
312
|
+
}
|
|
313
|
+
async setApprovals(runId, approvals) {
|
|
314
|
+
for (const a of approvals) {
|
|
315
|
+
if (a.type === 'agent-call') {
|
|
316
|
+
await this.toolCalls.updateOne({ _id: a.toolCallId }, {
|
|
317
|
+
$set: {
|
|
318
|
+
approvalStatus: 'pending',
|
|
319
|
+
runId,
|
|
320
|
+
approvalType: 'agent-call',
|
|
321
|
+
agentRunId: a.agentRunId,
|
|
322
|
+
displayToolName: a.displayToolName,
|
|
323
|
+
displayArgs: a.displayArgs,
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
await this.toolCalls.updateOne({ _id: a.toolCallId }, {
|
|
329
|
+
$set: {
|
|
330
|
+
approvalStatus: 'pending',
|
|
331
|
+
runId,
|
|
332
|
+
approvalType: 'tool-call',
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
mapRunRow(row, approvalRows) {
|
|
339
|
+
const pendingApprovals = approvalRows?.length
|
|
340
|
+
? approvalRows.map((a) => {
|
|
341
|
+
if (a.approvalType === 'agent-call') {
|
|
342
|
+
return {
|
|
343
|
+
type: 'agent-call',
|
|
344
|
+
toolCallId: a._id,
|
|
345
|
+
agentName: a.toolName,
|
|
346
|
+
agentRunId: a.agentRunId,
|
|
347
|
+
displayToolName: a.displayToolName,
|
|
348
|
+
displayArgs: a.displayArgs,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
type: 'tool-call',
|
|
353
|
+
toolCallId: a._id,
|
|
354
|
+
toolName: a.toolName,
|
|
355
|
+
args: a.args,
|
|
356
|
+
};
|
|
357
|
+
})
|
|
358
|
+
: undefined;
|
|
359
|
+
return {
|
|
360
|
+
runId: row._id,
|
|
361
|
+
agentName: row.agentName,
|
|
362
|
+
threadId: row.threadId,
|
|
363
|
+
resourceId: row.resourceId,
|
|
364
|
+
status: row.status,
|
|
365
|
+
errorMessage: row.errorMessage ?? undefined,
|
|
366
|
+
suspendReason: row.suspendReason,
|
|
367
|
+
missingRpcs: row.missingRpcs ?? undefined,
|
|
368
|
+
pendingApprovals,
|
|
369
|
+
usage: {
|
|
370
|
+
inputTokens: Number(row.usageInputTokens),
|
|
371
|
+
outputTokens: Number(row.usageOutputTokens),
|
|
372
|
+
model: row.usageModel,
|
|
373
|
+
},
|
|
374
|
+
createdAt: new Date(row.createdAt),
|
|
375
|
+
updatedAt: new Date(row.updatedAt),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async findRunByToolCallId(toolCallId) {
|
|
379
|
+
const tc = await this.toolCalls.findOne({
|
|
380
|
+
_id: toolCallId,
|
|
381
|
+
approvalStatus: 'pending',
|
|
382
|
+
});
|
|
383
|
+
if (!tc || !tc.runId)
|
|
384
|
+
return null;
|
|
385
|
+
const run = await this.getRun(tc.runId);
|
|
386
|
+
if (!run)
|
|
387
|
+
return null;
|
|
388
|
+
let approval;
|
|
389
|
+
if (tc.approvalType === 'agent-call') {
|
|
390
|
+
approval = {
|
|
391
|
+
type: 'agent-call',
|
|
392
|
+
toolCallId: tc._id,
|
|
393
|
+
agentName: tc.toolName,
|
|
394
|
+
agentRunId: tc.agentRunId,
|
|
395
|
+
displayToolName: tc.displayToolName,
|
|
396
|
+
displayArgs: tc.displayArgs,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
approval = {
|
|
401
|
+
type: 'tool-call',
|
|
402
|
+
toolCallId: tc._id,
|
|
403
|
+
toolName: tc.toolName,
|
|
404
|
+
args: tc.args,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return { run, approval };
|
|
408
|
+
}
|
|
409
|
+
async resolveApproval(toolCallId, status) {
|
|
410
|
+
// Only the caller that moves the tool call off 'pending' has claimed it.
|
|
411
|
+
const result = await this.toolCalls.updateOne({ _id: toolCallId, approvalStatus: 'pending' }, { $set: { approvalStatus: status } });
|
|
412
|
+
return result.modifiedCount > 0;
|
|
413
|
+
}
|
|
414
|
+
async saveScore(score) {
|
|
415
|
+
await this.agentRunScores.insertOne({
|
|
416
|
+
_id: crypto.randomUUID(),
|
|
417
|
+
runId: score.runId,
|
|
418
|
+
scorerName: score.scorerName,
|
|
419
|
+
score: score.score,
|
|
420
|
+
reason: score.reason ?? null,
|
|
421
|
+
metadata: score.metadata ?? null,
|
|
422
|
+
createdAt: new Date(),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async getScores(runId) {
|
|
426
|
+
const rows = await this.agentRunScores
|
|
427
|
+
.find({ runId })
|
|
428
|
+
.sort({ createdAt: 1 })
|
|
429
|
+
.toArray();
|
|
430
|
+
return rows.map((row) => ({
|
|
431
|
+
runId: row.runId,
|
|
432
|
+
scorerName: row.scorerName,
|
|
433
|
+
score: row.score,
|
|
434
|
+
reason: row.reason ?? undefined,
|
|
435
|
+
metadata: row.metadata ?? undefined,
|
|
436
|
+
createdAt: row.createdAt,
|
|
437
|
+
}));
|
|
438
|
+
}
|
|
439
|
+
async close() { }
|
|
440
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { AIStorageService, AIRunStateService, CreateRunInput } from '@pikku/core/services';
|
|
1
|
+
import type { AIStorageService, AIRunStateService, CreateRunInput, SaveScoreInput } from '@pikku/core/services';
|
|
2
2
|
import type { AIThread, AIMessage, AgentRunState } from '@pikku/core/ai-agent';
|
|
3
|
+
import type { AIRunScore } from '@pikku/core/ai-scorer';
|
|
3
4
|
import type { Db } from 'mongodb';
|
|
4
5
|
export declare class MongoDBAIStorageService implements AIStorageService, AIRunStateService {
|
|
5
6
|
private db;
|
|
@@ -9,6 +10,7 @@ export declare class MongoDBAIStorageService implements AIStorageService, AIRunS
|
|
|
9
10
|
private toolCalls;
|
|
10
11
|
private workingMemory;
|
|
11
12
|
private aiRuns;
|
|
13
|
+
private aiRunScores;
|
|
12
14
|
constructor(db: Db);
|
|
13
15
|
init(): Promise<void>;
|
|
14
16
|
createThread(resourceId: string, options?: {
|
|
@@ -37,5 +39,7 @@ export declare class MongoDBAIStorageService implements AIStorageService, AIRunS
|
|
|
37
39
|
approval: NonNullable<AgentRunState['pendingApprovals']>[number];
|
|
38
40
|
} | null>;
|
|
39
41
|
resolveApproval(toolCallId: string, status: 'approved' | 'denied'): Promise<boolean>;
|
|
42
|
+
saveScore(score: SaveScoreInput): Promise<void>;
|
|
43
|
+
getScores(runId: string): Promise<AIRunScore[]>;
|
|
40
44
|
close(): Promise<void>;
|
|
41
45
|
}
|