@statelyai/agent 2.0.0-next.3 → 2.0.0-next.5
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/.changeset/calm-beans-talk.md +5 -0
- package/.changeset/long-guests-explode.md +5 -0
- package/.changeset/nice-pants-rule.md +10 -0
- package/.changeset/odd-kiwis-compare.md +5 -0
- package/.changeset/pre.json +4 -0
- package/CHANGELOG.md +23 -0
- package/architecture.tldr +797 -0
- package/dist/index.d.mts +198 -140
- package/dist/index.d.ts +198 -140
- package/dist/index.js +4397 -148
- package/dist/index.mjs +4397 -149
- package/examples/chatbot.ts +9 -5
- package/examples/cot.ts +2 -4
- package/examples/jugs.ts +2 -2
- package/examples/learn-from-feedback.ts +7 -7
- package/examples/newspaper.ts +1 -1
- package/examples/rewoo.ts +62 -0
- package/examples/river-crossing.ts +2 -2
- package/examples/serverless.ts +71 -0
- package/examples/simple.ts +1 -1
- package/examples/ticTacToe.ts +6 -2
- package/examples/wiki.ts +2 -2
- package/package.json +14 -12
- package/readme.md +57 -0
- package/src/agent.test.ts +387 -30
- package/src/agent.ts +177 -64
- package/src/decide.test.ts +24 -2
- package/src/decide.ts +34 -78
- package/src/index.ts +1 -0
- package/src/{strategies/chainOfThought.ts → policies/chainOfThoughtPolicy.ts} +7 -9
- package/src/policies/index.ts +3 -0
- package/src/{strategies/shortestPath.test.ts → policies/shortestPathPolicy.test.ts} +2 -2
- package/src/{strategies/shortestPath.ts → policies/shortestPathPolicy.ts} +8 -8
- package/src/{strategies/simple.ts → policies/toolPolicy.ts} +27 -26
- package/src/text.ts +17 -22
- package/src/types.ts +162 -166
- package/src/agent-experimental.ts +0 -221
package/src/agent.test.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { test, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AgentDecision,
|
|
4
|
+
AgentFeedbackInput,
|
|
5
|
+
createAgent,
|
|
6
|
+
TypesFromAgent,
|
|
7
|
+
} from './';
|
|
3
8
|
import { createActor, createMachine } from 'xstate';
|
|
4
9
|
import { LanguageModelV1CallOptions } from 'ai';
|
|
5
10
|
import { z } from 'zod';
|
|
@@ -66,28 +71,44 @@ test('agent.addMessage() adds to message history', () => {
|
|
|
66
71
|
test('agent.addFeedback() adds to feedback', () => {
|
|
67
72
|
const agent = createAgent({
|
|
68
73
|
id: 'test',
|
|
69
|
-
events: {
|
|
74
|
+
events: {
|
|
75
|
+
play: z.object({
|
|
76
|
+
position: z.number(),
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
70
79
|
model: {} as any,
|
|
71
80
|
});
|
|
72
81
|
|
|
82
|
+
const decision: AgentDecision<typeof agent> = {
|
|
83
|
+
goal: 'Win the game',
|
|
84
|
+
decisionId: null,
|
|
85
|
+
episodeId: agent.episodeId,
|
|
86
|
+
goalState: { value: 'won' },
|
|
87
|
+
id: 'decision-1',
|
|
88
|
+
nextEvent: { type: 'play', position: 3 },
|
|
89
|
+
paths: [],
|
|
90
|
+
policy: 'simple',
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
};
|
|
93
|
+
|
|
73
94
|
const obs = agent.addObservation({
|
|
95
|
+
decisionId: decision.id,
|
|
74
96
|
prevState: { value: 'playing' },
|
|
75
|
-
state: { value: 'lost' },
|
|
76
97
|
event: { type: 'play', position: 3 },
|
|
77
|
-
|
|
98
|
+
state: { value: 'lost' },
|
|
78
99
|
});
|
|
79
100
|
|
|
80
101
|
const feedback = agent.addFeedback({
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
reward: 0,
|
|
103
|
+
decisionId: decision.id,
|
|
83
104
|
});
|
|
84
105
|
|
|
85
106
|
expect(feedback.episodeId).toEqual(agent.episodeId);
|
|
86
107
|
|
|
87
108
|
expect(agent.getFeedback()).toContainEqual(
|
|
88
109
|
expect.objectContaining({
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
reward: 0,
|
|
111
|
+
decisionId: decision.id,
|
|
91
112
|
episodeId: expect.any(String),
|
|
92
113
|
timestamp: expect.any(Number),
|
|
93
114
|
})
|
|
@@ -144,7 +165,7 @@ test('agent.addObservation() adds to observations (initial state)', () => {
|
|
|
144
165
|
);
|
|
145
166
|
});
|
|
146
167
|
|
|
147
|
-
test('agent.addObservation() adds to observations with machine hash', () => {
|
|
168
|
+
test.skip('agent.addObservation() adds to observations with machine hash', () => {
|
|
148
169
|
const agent = createAgent({
|
|
149
170
|
id: 'test',
|
|
150
171
|
events: {},
|
|
@@ -167,7 +188,6 @@ test('agent.addObservation() adds to observations with machine hash', () => {
|
|
|
167
188
|
prevState: { value: 'playing', context: {} },
|
|
168
189
|
event: { type: 'play', position: 3 },
|
|
169
190
|
state: { value: 'lost', context: {} },
|
|
170
|
-
machine,
|
|
171
191
|
goal: 'Win the game',
|
|
172
192
|
});
|
|
173
193
|
|
|
@@ -178,14 +198,13 @@ test('agent.addObservation() adds to observations with machine hash', () => {
|
|
|
178
198
|
prevState: { value: 'playing', context: {} },
|
|
179
199
|
event: { type: 'play', position: 3 },
|
|
180
200
|
state: { value: 'lost', context: {} },
|
|
181
|
-
machineHash: expect.any(String),
|
|
182
201
|
episodeId: expect.any(String),
|
|
183
202
|
timestamp: expect.any(Number),
|
|
184
203
|
})
|
|
185
204
|
);
|
|
186
205
|
});
|
|
187
206
|
|
|
188
|
-
test('agent.
|
|
207
|
+
test('agent.addInsight() adds to insights (with observation)', () => {
|
|
189
208
|
const agent = createAgent({
|
|
190
209
|
id: 'test',
|
|
191
210
|
events: {},
|
|
@@ -199,24 +218,26 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
|
|
|
199
218
|
goal: 'Win the game',
|
|
200
219
|
});
|
|
201
220
|
|
|
202
|
-
const
|
|
203
|
-
score: 0,
|
|
221
|
+
const insight = agent.addInsight({
|
|
204
222
|
observationId: observation.id,
|
|
223
|
+
attributes: {
|
|
224
|
+
advantage: 50,
|
|
225
|
+
},
|
|
205
226
|
});
|
|
206
227
|
|
|
207
|
-
expect(
|
|
228
|
+
expect(insight.episodeId).toEqual(agent.episodeId);
|
|
208
229
|
|
|
209
|
-
expect(agent.
|
|
230
|
+
expect(agent.getInsights()).toContainEqual(
|
|
210
231
|
expect.objectContaining({
|
|
211
|
-
|
|
232
|
+
attributes: { advantage: 50 },
|
|
212
233
|
observationId: observation.id,
|
|
213
234
|
episodeId: expect.any(String),
|
|
214
235
|
timestamp: expect.any(Number),
|
|
215
236
|
})
|
|
216
237
|
);
|
|
217
|
-
expect(agent.
|
|
238
|
+
expect(agent.getInsights()).toContainEqual(
|
|
218
239
|
expect.objectContaining({
|
|
219
|
-
|
|
240
|
+
attributes: { advantage: 50 },
|
|
220
241
|
observationId: observation.id,
|
|
221
242
|
episodeId: expect.any(String),
|
|
222
243
|
timestamp: expect.any(Number),
|
|
@@ -279,14 +300,25 @@ test('You can listen for feedback events', () => {
|
|
|
279
300
|
model: {} as any,
|
|
280
301
|
});
|
|
281
302
|
|
|
282
|
-
agent.
|
|
303
|
+
agent.onFeedback(fn);
|
|
283
304
|
|
|
284
305
|
agent.addFeedback({
|
|
285
|
-
|
|
286
|
-
|
|
306
|
+
reward: 1,
|
|
307
|
+
decisionId: 'dec-1',
|
|
308
|
+
comment: 'Good move',
|
|
309
|
+
attributes: { confidence: 'high' },
|
|
287
310
|
});
|
|
288
311
|
|
|
289
|
-
expect(fn).
|
|
312
|
+
expect(fn).toHaveBeenCalledWith(
|
|
313
|
+
expect.objectContaining({
|
|
314
|
+
reward: 1,
|
|
315
|
+
decisionId: 'dec-1',
|
|
316
|
+
comment: 'Good move',
|
|
317
|
+
attributes: { confidence: 'high' },
|
|
318
|
+
episodeId: expect.any(String),
|
|
319
|
+
timestamp: expect.any(Number),
|
|
320
|
+
})
|
|
321
|
+
);
|
|
290
322
|
});
|
|
291
323
|
|
|
292
324
|
test('You can listen for decision events', async () => {
|
|
@@ -425,20 +457,21 @@ test('agent.getDecisions() returns decisions from context', () => {
|
|
|
425
457
|
id: 'test',
|
|
426
458
|
events: {},
|
|
427
459
|
model: {} as any,
|
|
428
|
-
|
|
460
|
+
policy: async (agent) => {
|
|
429
461
|
return {
|
|
430
462
|
id: Date.now().toString(),
|
|
463
|
+
decisionId: null,
|
|
431
464
|
episodeId: agent.episodeId,
|
|
432
|
-
|
|
465
|
+
policy: 'test-policy',
|
|
433
466
|
goal: '',
|
|
434
|
-
goalState:
|
|
467
|
+
goalState: null,
|
|
435
468
|
paths: [
|
|
436
469
|
{
|
|
437
|
-
state:
|
|
470
|
+
state: null,
|
|
438
471
|
steps: [],
|
|
439
472
|
},
|
|
440
473
|
],
|
|
441
|
-
nextEvent:
|
|
474
|
+
nextEvent: null,
|
|
442
475
|
timestamp: Date.now(),
|
|
443
476
|
};
|
|
444
477
|
},
|
|
@@ -503,7 +536,6 @@ test('agent.observe() adds observations from actor snapshots', () => {
|
|
|
503
536
|
expect(agent.getObservations()).toContainEqual(
|
|
504
537
|
expect.objectContaining({
|
|
505
538
|
state: expect.objectContaining({ value: 'idle' }),
|
|
506
|
-
machineHash: expect.any(String),
|
|
507
539
|
})
|
|
508
540
|
);
|
|
509
541
|
|
|
@@ -512,9 +544,334 @@ test('agent.observe() adds observations from actor snapshots', () => {
|
|
|
512
544
|
prevState: expect.objectContaining({ value: 'idle' }),
|
|
513
545
|
event: { type: 'START' },
|
|
514
546
|
state: expect.objectContaining({ value: 'running' }),
|
|
515
|
-
machineHash: expect.any(String),
|
|
516
547
|
})
|
|
517
548
|
);
|
|
518
549
|
|
|
519
550
|
subscription.unsubscribe();
|
|
520
551
|
});
|
|
552
|
+
|
|
553
|
+
test('agent.addObservation() accepts custom episodeId', () => {
|
|
554
|
+
const agent = createAgent({
|
|
555
|
+
id: 'test',
|
|
556
|
+
events: {},
|
|
557
|
+
model: {} as any,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
const customEpisodeId = 'custom-episode-123';
|
|
561
|
+
const observation = agent.addObservation({
|
|
562
|
+
state: { value: 'playing' },
|
|
563
|
+
goal: 'Win the game',
|
|
564
|
+
episodeId: customEpisodeId,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
expect(observation.episodeId).toEqual(customEpisodeId);
|
|
568
|
+
expect(agent.getObservations()).toContainEqual(
|
|
569
|
+
expect.objectContaining({
|
|
570
|
+
episodeId: customEpisodeId,
|
|
571
|
+
})
|
|
572
|
+
);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test('agent.addFeedback() accepts custom episodeId', () => {
|
|
576
|
+
const agent = createAgent({
|
|
577
|
+
id: 'test',
|
|
578
|
+
events: {},
|
|
579
|
+
model: {} as any,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
const customEpisodeId = 'custom-episode-123';
|
|
583
|
+
const feedback = agent.addFeedback({
|
|
584
|
+
reward: 1,
|
|
585
|
+
decisionId: 'dec-1',
|
|
586
|
+
episodeId: customEpisodeId,
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
expect(feedback.episodeId).toEqual(customEpisodeId);
|
|
590
|
+
expect(agent.getFeedback()).toContainEqual(
|
|
591
|
+
expect.objectContaining({
|
|
592
|
+
episodeId: customEpisodeId,
|
|
593
|
+
})
|
|
594
|
+
);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
test('agent.addObservation() accepts decisionId', () => {
|
|
598
|
+
const agent = createAgent({
|
|
599
|
+
id: 'test',
|
|
600
|
+
events: {},
|
|
601
|
+
model: {} as any,
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
const decisionId = 'decision-123';
|
|
605
|
+
const observation = agent.addObservation({
|
|
606
|
+
state: { value: 'playing' },
|
|
607
|
+
goal: 'Win the game',
|
|
608
|
+
decisionId,
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
expect(observation.decisionId).toEqual(decisionId);
|
|
612
|
+
expect(agent.getObservations()).toContainEqual(
|
|
613
|
+
expect.objectContaining({
|
|
614
|
+
decisionId,
|
|
615
|
+
})
|
|
616
|
+
);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test('agent.addFeedback() accepts decisionId', () => {
|
|
620
|
+
const agent = createAgent({
|
|
621
|
+
id: 'test',
|
|
622
|
+
events: {},
|
|
623
|
+
model: {} as any,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
const decisionId = 'decision-123';
|
|
627
|
+
const feedback = agent.addFeedback({
|
|
628
|
+
reward: 1,
|
|
629
|
+
decisionId,
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
expect(feedback.decisionId).toEqual(decisionId);
|
|
633
|
+
expect(agent.getFeedback()).toContainEqual(
|
|
634
|
+
expect.objectContaining({
|
|
635
|
+
decisionId,
|
|
636
|
+
})
|
|
637
|
+
);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
test('You can listen for observation events', () => {
|
|
641
|
+
const fn = vi.fn();
|
|
642
|
+
const agent = createAgent({
|
|
643
|
+
id: 'test',
|
|
644
|
+
events: {},
|
|
645
|
+
model: {} as any,
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
agent.onObservation(fn);
|
|
649
|
+
|
|
650
|
+
agent.addObservation({
|
|
651
|
+
state: { value: 'playing' },
|
|
652
|
+
goal: 'Win the game',
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
expect(fn).toHaveBeenCalledWith(
|
|
656
|
+
expect.objectContaining({
|
|
657
|
+
state: { value: 'playing' },
|
|
658
|
+
episodeId: expect.any(String),
|
|
659
|
+
timestamp: expect.any(Number),
|
|
660
|
+
})
|
|
661
|
+
);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
test('You can listen for decision events', () => {
|
|
665
|
+
const fn = vi.fn();
|
|
666
|
+
const agent = createAgent({
|
|
667
|
+
id: 'test',
|
|
668
|
+
events: {
|
|
669
|
+
MOVE: z.object({}),
|
|
670
|
+
},
|
|
671
|
+
model: {} as any,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
agent.onDecision(fn);
|
|
675
|
+
|
|
676
|
+
const decision = {
|
|
677
|
+
id: 'decision-1',
|
|
678
|
+
decisionId: null,
|
|
679
|
+
episodeId: agent.episodeId,
|
|
680
|
+
policy: 'test-policy',
|
|
681
|
+
goal: 'Win the game',
|
|
682
|
+
goalState: { value: 'won' },
|
|
683
|
+
paths: [],
|
|
684
|
+
nextEvent: { type: 'MOVE' },
|
|
685
|
+
timestamp: Date.now(),
|
|
686
|
+
} satisfies AgentDecision<typeof agent>;
|
|
687
|
+
|
|
688
|
+
agent.addDecision(decision);
|
|
689
|
+
|
|
690
|
+
expect(fn).toHaveBeenCalledWith(
|
|
691
|
+
expect.objectContaining({
|
|
692
|
+
id: 'decision-1',
|
|
693
|
+
episodeId: agent.episodeId,
|
|
694
|
+
policy: 'test-policy',
|
|
695
|
+
goal: 'Win the game',
|
|
696
|
+
nextEvent: { type: 'MOVE' },
|
|
697
|
+
})
|
|
698
|
+
);
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
test('Event listeners can be unsubscribed (onObservation)', () => {
|
|
702
|
+
const fn = vi.fn();
|
|
703
|
+
const agent = createAgent({
|
|
704
|
+
id: 'test',
|
|
705
|
+
events: {},
|
|
706
|
+
model: {} as any,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
const subscription = agent.onObservation(fn);
|
|
710
|
+
|
|
711
|
+
agent.addObservation({
|
|
712
|
+
state: { value: 'playing' },
|
|
713
|
+
goal: 'Win the game',
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
717
|
+
|
|
718
|
+
subscription.unsubscribe();
|
|
719
|
+
|
|
720
|
+
agent.addObservation({
|
|
721
|
+
state: { value: 'playing' },
|
|
722
|
+
goal: 'Win the game again',
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
expect(fn).toHaveBeenCalledTimes(1); // Still only called once
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
test('Event listeners can be unsubscribed (onDecision)', () => {
|
|
729
|
+
const fn = vi.fn();
|
|
730
|
+
const agent = createAgent({
|
|
731
|
+
id: 'test',
|
|
732
|
+
events: {
|
|
733
|
+
MOVE: z.object({}),
|
|
734
|
+
},
|
|
735
|
+
model: {} as any,
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
const subscription = agent.onDecision(fn);
|
|
739
|
+
|
|
740
|
+
const decision = {
|
|
741
|
+
id: 'decision-1',
|
|
742
|
+
decisionId: null,
|
|
743
|
+
episodeId: agent.episodeId,
|
|
744
|
+
policy: 'test-policy',
|
|
745
|
+
goal: 'Win the game',
|
|
746
|
+
goalState: { value: 'won' },
|
|
747
|
+
paths: [],
|
|
748
|
+
nextEvent: { type: 'MOVE' },
|
|
749
|
+
timestamp: Date.now(),
|
|
750
|
+
} satisfies AgentDecision<typeof agent>;
|
|
751
|
+
|
|
752
|
+
agent.addDecision(decision);
|
|
753
|
+
|
|
754
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
755
|
+
|
|
756
|
+
subscription.unsubscribe();
|
|
757
|
+
|
|
758
|
+
agent.addDecision({
|
|
759
|
+
...decision,
|
|
760
|
+
id: 'decision-2',
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
expect(fn).toHaveBeenCalledTimes(1); // Still only called once
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
test('Event listeners can be unsubscribed (onFeedback)', () => {
|
|
767
|
+
const fn = vi.fn();
|
|
768
|
+
const agent = createAgent({
|
|
769
|
+
id: 'test',
|
|
770
|
+
events: {},
|
|
771
|
+
model: {} as any,
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
const subscription = agent.onFeedback(fn);
|
|
775
|
+
|
|
776
|
+
agent.addFeedback({
|
|
777
|
+
reward: 1,
|
|
778
|
+
decisionId: 'dec-1',
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
782
|
+
|
|
783
|
+
subscription.unsubscribe();
|
|
784
|
+
|
|
785
|
+
agent.addFeedback({
|
|
786
|
+
reward: 0,
|
|
787
|
+
decisionId: 'dec-2',
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
expect(fn).toHaveBeenCalledTimes(1); // Still only called once
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
test('Feedback events include optional fields', () => {
|
|
794
|
+
const fn = vi.fn();
|
|
795
|
+
const agent = createAgent({
|
|
796
|
+
id: 'test',
|
|
797
|
+
events: {},
|
|
798
|
+
model: {} as any,
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
agent.onFeedback(fn);
|
|
802
|
+
|
|
803
|
+
// Test with minimal feedback
|
|
804
|
+
agent.addFeedback({
|
|
805
|
+
reward: 1,
|
|
806
|
+
decisionId: 'dec-1',
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
expect(fn).toHaveBeenCalledWith(
|
|
810
|
+
expect.objectContaining({
|
|
811
|
+
reward: 1,
|
|
812
|
+
decisionId: 'dec-1',
|
|
813
|
+
comment: undefined,
|
|
814
|
+
attributes: {},
|
|
815
|
+
episodeId: expect.any(String),
|
|
816
|
+
timestamp: expect.any(Number),
|
|
817
|
+
})
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
// Test with all optional fields
|
|
821
|
+
agent.addFeedback({
|
|
822
|
+
reward: 0,
|
|
823
|
+
decisionId: 'dec-2',
|
|
824
|
+
comment: 'Could be better',
|
|
825
|
+
attributes: { reason: 'suboptimal' },
|
|
826
|
+
} satisfies AgentFeedbackInput);
|
|
827
|
+
|
|
828
|
+
expect(fn).toHaveBeenLastCalledWith(
|
|
829
|
+
expect.objectContaining({
|
|
830
|
+
reward: 0,
|
|
831
|
+
decisionId: 'dec-2',
|
|
832
|
+
comment: 'Could be better',
|
|
833
|
+
attributes: { reason: 'suboptimal' },
|
|
834
|
+
episodeId: expect.any(String),
|
|
835
|
+
timestamp: expect.any(Number),
|
|
836
|
+
})
|
|
837
|
+
);
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
test('Feedback events maintain episodeId consistency', () => {
|
|
841
|
+
const fn = vi.fn();
|
|
842
|
+
const customEpisodeId = 'custom-episode-123';
|
|
843
|
+
|
|
844
|
+
const agent = createAgent({
|
|
845
|
+
id: 'test',
|
|
846
|
+
events: {},
|
|
847
|
+
model: {} as any,
|
|
848
|
+
episodeId: customEpisodeId,
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
agent.onFeedback(fn);
|
|
852
|
+
|
|
853
|
+
agent.addFeedback({
|
|
854
|
+
reward: 1,
|
|
855
|
+
decisionId: 'dec-1',
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
expect(fn).toHaveBeenCalledWith(
|
|
859
|
+
expect.objectContaining({
|
|
860
|
+
episodeId: customEpisodeId,
|
|
861
|
+
})
|
|
862
|
+
);
|
|
863
|
+
|
|
864
|
+
// Test with explicit different episodeId
|
|
865
|
+
const differentEpisodeId = 'different-episode-456';
|
|
866
|
+
agent.addFeedback({
|
|
867
|
+
reward: 0,
|
|
868
|
+
decisionId: 'dec-2',
|
|
869
|
+
episodeId: differentEpisodeId,
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
expect(fn).toHaveBeenLastCalledWith(
|
|
873
|
+
expect.objectContaining({
|
|
874
|
+
episodeId: differentEpisodeId,
|
|
875
|
+
})
|
|
876
|
+
);
|
|
877
|
+
});
|