@wabot-dev/framework 0.9.27 → 0.9.80
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/README.md +27 -0
- package/dist/src/feature/chat-controller/runChatControllers.js +4 -1
- package/dist/src/feature/rest-controller/runRestControllers.js +11 -6
- package/dist/src/index.d.ts +9 -1
- package/dist/src/index.js +2 -2
- package/dist/src/testing/LlmJudge.js +93 -0
- package/dist/src/testing/MockChatAdapter.js +68 -0
- package/dist/src/testing/TestChatMemory.js +73 -0
- package/dist/src/testing/asyncHarness.js +66 -0
- package/dist/src/testing/auth.js +114 -0
- package/dist/src/testing/chatBotHarness.js +88 -0
- package/dist/src/testing/chatControllerHarness.js +94 -0
- package/dist/src/testing/conformance/chatAdapterConformanceCases.js +656 -0
- package/dist/src/testing/fixtures.js +53 -0
- package/dist/src/testing/helpers.js +42 -0
- package/dist/src/testing/index.d.ts +776 -0
- package/dist/src/testing/index.js +13 -0
- package/dist/src/testing/repositories.js +34 -0
- package/dist/src/testing/restHarness.js +127 -0
- package/dist/src/testing/testImageBase64.js +5 -0
- package/dist/src/testing/validation.js +66 -0
- package/package.json +19 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Auth } from '../core/auth/Auth.js';
|
|
2
|
+
import { container } from '../core/injection/index.js';
|
|
3
|
+
import { ChatAdapter } from '../feature/chat-bot/ChatAdapter.js';
|
|
4
|
+
import '../feature/chat-bot/ChatAdapterRegistry.js';
|
|
5
|
+
import { ChatBot } from '../feature/chat-bot/ChatBot.js';
|
|
6
|
+
import { ChatMemory } from '../feature/chat-bot/ChatMemory.js';
|
|
7
|
+
import '../feature/chat-bot/ChatOperator.js';
|
|
8
|
+
import '../feature/chat-bot/UnionChatAdapter.js';
|
|
9
|
+
import '../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
10
|
+
import 'uuid';
|
|
11
|
+
import '../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
12
|
+
import '../core/error/setupErrorHandlers.js';
|
|
13
|
+
import '../feature/mindset/metadata/MindsetMetadataStore.js';
|
|
14
|
+
import { Mindset } from '../feature/mindset/IMindset.js';
|
|
15
|
+
import { MindsetOperator } from '../feature/mindset/MindsetOperator.js';
|
|
16
|
+
import { humanMessage } from './fixtures.js';
|
|
17
|
+
import { MockChatAdapter } from './MockChatAdapter.js';
|
|
18
|
+
import { TestChatMemory } from './TestChatMemory.js';
|
|
19
|
+
import { Container } from '../core/injection/Container.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Runs a real ChatBot (real MindsetOperator, system prompt, tool loop and
|
|
23
|
+
* argument validation) against an in-RAM memory and a pluggable adapter.
|
|
24
|
+
*/
|
|
25
|
+
class ChatBotHarness {
|
|
26
|
+
adapter;
|
|
27
|
+
memory = new TestChatMemory();
|
|
28
|
+
container;
|
|
29
|
+
chatBot;
|
|
30
|
+
operator;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.adapter = options.adapter ?? new MockChatAdapter();
|
|
33
|
+
const child = container.createChildContainer();
|
|
34
|
+
child.register(Container, { useValue: child });
|
|
35
|
+
child.register(Mindset, { useClass: options.mindset });
|
|
36
|
+
child.registerInstance(ChatMemory, this.memory);
|
|
37
|
+
child.registerInstance(ChatAdapter, this.adapter);
|
|
38
|
+
for (const [token, instance] of options.register ?? []) {
|
|
39
|
+
child.registerInstance(token, instance);
|
|
40
|
+
}
|
|
41
|
+
if (options.authInfo) {
|
|
42
|
+
const auth = child.resolve(Auth);
|
|
43
|
+
auth.assign(options.authInfo);
|
|
44
|
+
}
|
|
45
|
+
this.container = child;
|
|
46
|
+
this.chatBot = child.resolve(ChatBot);
|
|
47
|
+
this.operator = child.resolve(MindsetOperator);
|
|
48
|
+
}
|
|
49
|
+
/** Send a human message and collect everything the bot did in response. */
|
|
50
|
+
async send(message) {
|
|
51
|
+
const itemsBefore = this.memory.all().length;
|
|
52
|
+
const replies = [];
|
|
53
|
+
await this.chatBot.sendMessage(humanMessage(message), async (reply) => {
|
|
54
|
+
replies.push(reply);
|
|
55
|
+
});
|
|
56
|
+
const items = this.memory.allData().slice(itemsBefore);
|
|
57
|
+
const toolCalls = items
|
|
58
|
+
.filter((item) => item.type === 'functionCall')
|
|
59
|
+
.map((item) => item.functionCall);
|
|
60
|
+
return { replies, toolCalls, items };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Execute a single mindset tool directly (real argument validation and
|
|
64
|
+
* module resolution), without scripting a whole conversation.
|
|
65
|
+
* Returns the string result exactly as the LLM would receive it.
|
|
66
|
+
*/
|
|
67
|
+
async callTool(name, args = {}) {
|
|
68
|
+
const argsJson = typeof args === 'string' ? args : JSON.stringify(args);
|
|
69
|
+
return await this.operator.callFunction(name, argsJson);
|
|
70
|
+
}
|
|
71
|
+
/** Full chat history (data form) accumulated across turns. */
|
|
72
|
+
history() {
|
|
73
|
+
return this.memory.allData();
|
|
74
|
+
}
|
|
75
|
+
/** The real system prompt the mindset produces. */
|
|
76
|
+
async systemPrompt() {
|
|
77
|
+
return await this.operator.systemPrompt();
|
|
78
|
+
}
|
|
79
|
+
/** The real tool definitions derived from the mindset modules. */
|
|
80
|
+
tools() {
|
|
81
|
+
return this.operator.tools();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function createChatBotHarness(options) {
|
|
85
|
+
return new ChatBotHarness(options);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { ChatBotHarness, createChatBotHarness };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { InMemoryLocker } from '../addon/lock/InMemoryLocker.js';
|
|
2
|
+
import { container } from '../core/injection/index.js';
|
|
3
|
+
import { Locker } from '../core/lock/Locker.js';
|
|
4
|
+
import { ChatAdapter } from '../feature/chat-bot/ChatAdapter.js';
|
|
5
|
+
import '../feature/chat-bot/ChatAdapterRegistry.js';
|
|
6
|
+
import '../feature/chat-bot/ChatBot.js';
|
|
7
|
+
import '../feature/chat-bot/ChatOperator.js';
|
|
8
|
+
import { ChatRepository } from '../feature/chat-bot/ChatRepository.js';
|
|
9
|
+
import '../feature/chat-bot/UnionChatAdapter.js';
|
|
10
|
+
import '../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
11
|
+
import 'uuid';
|
|
12
|
+
import '../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
13
|
+
import '../core/error/setupErrorHandlers.js';
|
|
14
|
+
import '../feature/chat-controller/metadata/ControllerMetadataStore.js';
|
|
15
|
+
import { ChatResolver } from '../feature/chat-controller/ChatResolver.js';
|
|
16
|
+
import { prepareChatContainer } from '../feature/chat-controller/runChatControllers.js';
|
|
17
|
+
import { humanMessage } from './fixtures.js';
|
|
18
|
+
import { MockChatAdapter } from './MockChatAdapter.js';
|
|
19
|
+
import { TestChatRepository } from './TestChatMemory.js';
|
|
20
|
+
import { Container } from '../core/injection/Container.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Drives a @chatController end-to-end without a real channel: resolves the
|
|
24
|
+
* Chat, prepares the per-message container (same code path as production,
|
|
25
|
+
* including @chatBot injection) and invokes the controller method.
|
|
26
|
+
*/
|
|
27
|
+
class ChatControllerHarness {
|
|
28
|
+
adapter;
|
|
29
|
+
chatRepository = new TestChatRepository();
|
|
30
|
+
chatConnection;
|
|
31
|
+
channelContainer;
|
|
32
|
+
options;
|
|
33
|
+
constructor(options) {
|
|
34
|
+
this.options = options;
|
|
35
|
+
this.adapter = options.adapter ?? new MockChatAdapter();
|
|
36
|
+
this.chatConnection = options.chatConnection ?? {
|
|
37
|
+
chatType: 'PRIVATE',
|
|
38
|
+
channelName: 'TestChannel',
|
|
39
|
+
id: 'test-chat',
|
|
40
|
+
};
|
|
41
|
+
const child = container.createChildContainer();
|
|
42
|
+
child.register(Container, { useValue: child });
|
|
43
|
+
child.register(Locker, { useToken: InMemoryLocker });
|
|
44
|
+
child.registerInstance(ChatRepository, this.chatRepository);
|
|
45
|
+
child.registerInstance(ChatAdapter, this.adapter);
|
|
46
|
+
for (const [token, instance] of options.register ?? []) {
|
|
47
|
+
child.registerInstance(token, instance);
|
|
48
|
+
}
|
|
49
|
+
this.channelContainer = child;
|
|
50
|
+
}
|
|
51
|
+
/** Deliver a message to a controller method, as if it came from a channel. */
|
|
52
|
+
async invoke(methodName, message) {
|
|
53
|
+
const controllerCtor = this.options.controller;
|
|
54
|
+
if (typeof controllerCtor.prototype[methodName] !== 'function') {
|
|
55
|
+
throw new Error(`ChatControllerHarness: ${controllerCtor.name} has no method '${methodName}'`);
|
|
56
|
+
}
|
|
57
|
+
const chatResolver = this.channelContainer.resolve(ChatResolver);
|
|
58
|
+
const chat = await chatResolver.resolve(this.chatConnection);
|
|
59
|
+
const memory = await this.chatRepository.findMemory(chat.id);
|
|
60
|
+
const itemsBefore = memory ? memory.all().length : 0;
|
|
61
|
+
const replies = [];
|
|
62
|
+
const receivedMessage = {
|
|
63
|
+
message: humanMessage(message),
|
|
64
|
+
reply: async (reply) => {
|
|
65
|
+
replies.push(reply);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const chatContainer = await prepareChatContainer(this.channelContainer, {
|
|
69
|
+
chat,
|
|
70
|
+
authInfo: this.options.authInfo,
|
|
71
|
+
...receivedMessage,
|
|
72
|
+
});
|
|
73
|
+
const controller = chatContainer.resolve(controllerCtor);
|
|
74
|
+
await controller[methodName](receivedMessage);
|
|
75
|
+
const items = (await this.history()).slice(itemsBefore);
|
|
76
|
+
const toolCalls = items
|
|
77
|
+
.filter((item) => item.type === 'functionCall')
|
|
78
|
+
.map((item) => item.functionCall);
|
|
79
|
+
return { replies, toolCalls, items };
|
|
80
|
+
}
|
|
81
|
+
/** Full chat history (data form) of the harness conversation. */
|
|
82
|
+
async history() {
|
|
83
|
+
const chat = await this.chatRepository.findByConnection(this.chatConnection);
|
|
84
|
+
if (!chat)
|
|
85
|
+
return [];
|
|
86
|
+
const memory = await this.chatRepository.findMemory(chat.id);
|
|
87
|
+
return memory ? memory.allData() : [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function createChatControllerHarness(options) {
|
|
91
|
+
return new ChatControllerHarness(options);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { ChatControllerHarness, createChatControllerHarness };
|