@sprucelabs/sprucebot-llm 8.4.101 → 8.5.1
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/build/bots/SprucebotLlmBotImpl.d.ts +2 -2
- package/build/bots/SprucebotLlmBotImpl.js +22 -4
- package/build/bots/adapters/OpenAi.d.ts +0 -1
- package/build/bots/adapters/OpenAi.js +6 -4
- package/build/bots/adapters/OpenAiMessageBuilder.d.ts +1 -0
- package/build/bots/adapters/OpenAiMessageBuilder.js +22 -4
- package/build/chatWithImages.d.ts +1 -0
- package/build/chatWithImages.js +42 -0
- package/build/esm/bots/SprucebotLlmBotImpl.d.ts +2 -2
- package/build/esm/bots/SprucebotLlmBotImpl.js +22 -4
- package/build/esm/bots/adapters/OpenAi.d.ts +0 -1
- package/build/esm/bots/adapters/OpenAi.js +6 -4
- package/build/esm/bots/adapters/OpenAiMessageBuilder.d.ts +1 -0
- package/build/esm/bots/adapters/OpenAiMessageBuilder.js +22 -4
- package/build/esm/bots/templates.js +2 -2
- package/build/esm/chatWithImages.d.ts +1 -0
- package/build/esm/chatWithImages.js +46 -0
- package/build/esm/llm.types.d.ts +8 -2
- package/build/esm/parsingResponses/ResponseParser.d.ts +2 -2
- package/build/esm/parsingResponses/ResponseParser.js +4 -4
- package/build/esm/parsingResponses/renderPlaceholder.d.ts +1 -1
- package/build/esm/parsingResponses/renderPlaceholder.js +1 -1
- package/build/examples/images/image1.png +0 -0
- package/build/examples/images/image2.png +0 -0
- package/build/llm.types.d.ts +8 -2
- package/build/parsingResponses/ResponseParser.d.ts +2 -2
- package/build/parsingResponses/ResponseParser.js +1 -1
- package/build/parsingResponses/renderPlaceholder.d.ts +1 -1
- package/build/parsingResponses/renderPlaceholder.js +2 -2
- package/package.json +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AbstractEventEmitter } from '@sprucelabs/mercury-event-emitter';
|
|
2
2
|
import { Schema, SchemaValues } from '@sprucelabs/schema';
|
|
3
|
-
import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, MessageResponseCallback, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
|
|
3
|
+
import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, MessageResponseCallback, SendMessage, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
|
|
4
4
|
export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends AbstractEventEmitter<LlmEventContract> implements SprucebotLlmBot<StateSchema, State> {
|
|
5
5
|
static messageMemoryLimit: number;
|
|
6
6
|
protected adapter: LlmAdapter;
|
|
@@ -15,7 +15,7 @@ export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, St
|
|
|
15
15
|
markAsDone(): void;
|
|
16
16
|
getIsDone(): boolean;
|
|
17
17
|
serialize(): SerializedBot<StateSchema, State>;
|
|
18
|
-
sendMessage(message:
|
|
18
|
+
sendMessage(message: SendMessage, cb?: MessageResponseCallback): Promise<string>;
|
|
19
19
|
private optionallyUpdateState;
|
|
20
20
|
private parseResponse;
|
|
21
21
|
private sendMessageToAdapter;
|
|
@@ -45,10 +45,18 @@ class SprucebotLlmBotImpl extends mercury_event_emitter_1.AbstractEventEmitter {
|
|
|
45
45
|
}
|
|
46
46
|
async sendMessage(message, cb) {
|
|
47
47
|
(0, schema_1.assertOptions)({ message }, ['message']);
|
|
48
|
-
|
|
48
|
+
const llmMessage = {
|
|
49
49
|
from: 'Me',
|
|
50
|
-
message,
|
|
51
|
-
}
|
|
50
|
+
message: '',
|
|
51
|
+
};
|
|
52
|
+
if (typeof message === 'string') {
|
|
53
|
+
llmMessage.message = message;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
llmMessage.message = message.imageDescription;
|
|
57
|
+
llmMessage.imageBase64 = message.imageBase64;
|
|
58
|
+
}
|
|
59
|
+
this.trackMessage(llmMessage);
|
|
52
60
|
const { model, callbacks } = this.skill?.serialize() ?? {};
|
|
53
61
|
const response = await this.sendMessageToAdapter(model);
|
|
54
62
|
let parsedMessage;
|
|
@@ -76,7 +84,17 @@ class SprucebotLlmBotImpl extends mercury_event_emitter_1.AbstractEventEmitter {
|
|
|
76
84
|
});
|
|
77
85
|
cb?.(parsedMessage);
|
|
78
86
|
if (callbackResults) {
|
|
79
|
-
|
|
87
|
+
let message;
|
|
88
|
+
if (typeof callbackResults === 'string') {
|
|
89
|
+
message = `API Results: ${callbackResults}`;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
message = {
|
|
93
|
+
imageBase64: callbackResults.imageBase64,
|
|
94
|
+
imageDescription: `API Results: ${callbackResults.imageDescription}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
await this.sendMessage(message, cb);
|
|
80
98
|
}
|
|
81
99
|
return parsedMessage;
|
|
82
100
|
}
|
|
@@ -3,7 +3,6 @@ import { LlmAdapter, SendMessageOptions, SprucebotLlmBot } from '../../llm.types
|
|
|
3
3
|
export declare class OpenAiAdapter implements LlmAdapter {
|
|
4
4
|
static OpenAI: typeof OpenAI;
|
|
5
5
|
private api;
|
|
6
|
-
private log;
|
|
7
6
|
protected constructor(apiKey: string);
|
|
8
7
|
static Adapter(apiKey: string): OpenAiAdapter;
|
|
9
8
|
sendMessage(bot: SprucebotLlmBot, options?: SendMessageOptions): Promise<string>;
|
|
@@ -5,12 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.MESSAGE_RESPONSE_ERROR_MESSAGE = exports.OpenAiAdapter = void 0;
|
|
7
7
|
const schema_1 = require("@sprucelabs/schema");
|
|
8
|
-
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
|
|
9
8
|
const openai_1 = __importDefault(require("openai"));
|
|
10
9
|
const OpenAiMessageBuilder_1 = __importDefault(require("./OpenAiMessageBuilder"));
|
|
11
10
|
class OpenAiAdapter {
|
|
11
|
+
// private log = buildLog('SprucebotLLM::OpenAiAdapter')
|
|
12
12
|
constructor(apiKey) {
|
|
13
|
-
this.log = (0, spruce_skill_utils_1.buildLog)('SprucebotLLM::OpenAiAdapter');
|
|
14
13
|
(0, schema_1.assertOptions)({ apiKey }, ['apiKey']);
|
|
15
14
|
this.api = new OpenAiAdapter.OpenAI({ apiKey });
|
|
16
15
|
}
|
|
@@ -20,14 +19,17 @@ class OpenAiAdapter {
|
|
|
20
19
|
async sendMessage(bot, options) {
|
|
21
20
|
const messageBuilder = OpenAiMessageBuilder_1.default.Builder(bot);
|
|
22
21
|
const messages = messageBuilder.buildMessages();
|
|
23
|
-
this.log.info(
|
|
22
|
+
// this.log.info(
|
|
23
|
+
// 'Sending message to OpenAI',
|
|
24
|
+
// JSON.stringify(messages, null, 2)
|
|
25
|
+
// )
|
|
24
26
|
const response = await this.api.chat.completions.create({
|
|
25
27
|
messages,
|
|
26
28
|
model: options?.model ?? 'gpt-4o',
|
|
27
29
|
});
|
|
28
30
|
const message = response.choices?.[0]?.message?.content?.trim() ??
|
|
29
31
|
exports.MESSAGE_RESPONSE_ERROR_MESSAGE;
|
|
30
|
-
this.log.info('Received response from OpenAI', message)
|
|
32
|
+
// this.log.info('Received response from OpenAI', message)
|
|
31
33
|
return message;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -6,6 +6,7 @@ export default class OpenAiMessageBuilder {
|
|
|
6
6
|
static Builder(bot: SprucebotLlmBot): OpenAiMessageBuilder;
|
|
7
7
|
buildMessages(): ChatCompletionMessageParam[];
|
|
8
8
|
private buildChatHistoryMessages;
|
|
9
|
+
private mapMessageToCompletion;
|
|
9
10
|
private buildFirstMessage;
|
|
10
11
|
private buildSkillMessages;
|
|
11
12
|
private buildCallbacksMessage;
|
|
@@ -23,10 +23,28 @@ class OpenAiMessageBuilder {
|
|
|
23
23
|
if (limit > 0) {
|
|
24
24
|
messagesBeingConsidered = messages.slice(Math.max(messages.length - limit, 0));
|
|
25
25
|
}
|
|
26
|
-
return messagesBeingConsidered.map((message) => (
|
|
26
|
+
return messagesBeingConsidered.map((message) => this.mapMessageToCompletion(message));
|
|
27
|
+
}
|
|
28
|
+
mapMessageToCompletion(message) {
|
|
29
|
+
let content = message.message;
|
|
30
|
+
if (message.imageBase64) {
|
|
31
|
+
content = [
|
|
32
|
+
{
|
|
33
|
+
type: 'text',
|
|
34
|
+
text: message.message,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'image_url',
|
|
38
|
+
image_url: {
|
|
39
|
+
url: `data:image/png;base64,${message.imageBase64}`,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
27
45
|
role: message.from === 'Me' ? 'user' : 'assistant',
|
|
28
|
-
content
|
|
29
|
-
}
|
|
46
|
+
content,
|
|
47
|
+
};
|
|
30
48
|
}
|
|
31
49
|
buildFirstMessage(youAre) {
|
|
32
50
|
return {
|
|
@@ -83,7 +101,7 @@ class OpenAiMessageBuilder {
|
|
|
83
101
|
const api = `<APIReference>\n\n${descriptions.join('\n\n')}</APIReference>`;
|
|
84
102
|
return {
|
|
85
103
|
role: 'system',
|
|
86
|
-
content: `You have an API available to you to lookup answers. When you need the response of the function call to proceed, you can call a function using a custom markup we created that looks like this: <<
|
|
104
|
+
content: `You have an API available to you to lookup answers. When you need the response of the function call to proceed, you can call a function using a custom markup we created that looks like this: <<FunctionName/>>. The API will respond with the results and then you can continue the conversation with your new knowledge. If the api call has parameters, call it like this: <<FunctionName>>{{parametersJsonEncoded}}<</FunctionName>>. Make sure to json encode the data and drop it between the function tags. The API is as follows (in xml format):\n\n${api}`,
|
|
87
105
|
};
|
|
88
106
|
}
|
|
89
107
|
buildPleaseKeepInMindMessage(pleaseKeepInMindThat) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
|
+
const OpenAi_1 = require("./bots/adapters/OpenAi");
|
|
9
|
+
const SprucebotLlmFactory_1 = __importDefault(require("./bots/SprucebotLlmFactory"));
|
|
10
|
+
dotenv_1.default.config();
|
|
11
|
+
void (async () => {
|
|
12
|
+
console.clear();
|
|
13
|
+
const image1 = fs_1.default.readFileSync('build/examples/images/image1.png');
|
|
14
|
+
const image2 = fs_1.default.readFileSync('build/examples/images/image2.png');
|
|
15
|
+
const base64Image1 = image1.toString('base64');
|
|
16
|
+
const base64Image2 = image2.toString('base64');
|
|
17
|
+
const adapter = OpenAi_1.OpenAiAdapter.Adapter(process.env.OPEN_AI_API_KEY);
|
|
18
|
+
const bots = SprucebotLlmFactory_1.default.Factory(adapter);
|
|
19
|
+
const skill = bots.Skill({
|
|
20
|
+
weAreDoneWhen: 'you have described both images to me',
|
|
21
|
+
yourJobIfYouChooseToAcceptItIs: 'to review the image i send you, invoke the getNextImage function and then describe that image too.',
|
|
22
|
+
callbacks: {
|
|
23
|
+
getNextImage: {
|
|
24
|
+
cb: async () => {
|
|
25
|
+
return {
|
|
26
|
+
imageBase64: base64Image2,
|
|
27
|
+
imageDescription: 'A beautiful sunset over the mountains.',
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
useThisWhenever: 'you want to get the next image',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const bot = bots.Bot({
|
|
35
|
+
skill,
|
|
36
|
+
youAre: "a bot named Sprucebot that is in test mode. At the start of every conversation, you introduce yourself and announce that you are in test mode so I don't get confused! You are both hip and adorable. You say things like, 'Jeepers' and 'Golly' or even 'Jeezey peezy'!",
|
|
37
|
+
});
|
|
38
|
+
await bot.sendMessage({
|
|
39
|
+
imageBase64: base64Image1,
|
|
40
|
+
imageDescription: 'The first image',
|
|
41
|
+
}, (message) => console.log('>', message));
|
|
42
|
+
})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AbstractEventEmitter } from '@sprucelabs/mercury-event-emitter';
|
|
2
2
|
import { Schema, SchemaValues } from '@sprucelabs/schema';
|
|
3
|
-
import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, MessageResponseCallback, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
|
|
3
|
+
import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, MessageResponseCallback, SendMessage, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
|
|
4
4
|
export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends AbstractEventEmitter<LlmEventContract> implements SprucebotLlmBot<StateSchema, State> {
|
|
5
5
|
static messageMemoryLimit: number;
|
|
6
6
|
protected adapter: LlmAdapter;
|
|
@@ -15,7 +15,7 @@ export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, St
|
|
|
15
15
|
markAsDone(): void;
|
|
16
16
|
getIsDone(): boolean;
|
|
17
17
|
serialize(): SerializedBot<StateSchema, State>;
|
|
18
|
-
sendMessage(message:
|
|
18
|
+
sendMessage(message: SendMessage, cb?: MessageResponseCallback): Promise<string>;
|
|
19
19
|
private optionallyUpdateState;
|
|
20
20
|
private parseResponse;
|
|
21
21
|
private sendMessageToAdapter;
|
|
@@ -49,10 +49,18 @@ class SprucebotLlmBotImpl extends AbstractEventEmitter {
|
|
|
49
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
50
50
|
var _a, _b, _c;
|
|
51
51
|
assertOptions({ message }, ['message']);
|
|
52
|
-
|
|
52
|
+
const llmMessage = {
|
|
53
53
|
from: 'Me',
|
|
54
|
-
message,
|
|
55
|
-
}
|
|
54
|
+
message: '',
|
|
55
|
+
};
|
|
56
|
+
if (typeof message === 'string') {
|
|
57
|
+
llmMessage.message = message;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
llmMessage.message = message.imageDescription;
|
|
61
|
+
llmMessage.imageBase64 = message.imageBase64;
|
|
62
|
+
}
|
|
63
|
+
this.trackMessage(llmMessage);
|
|
56
64
|
const { model, callbacks } = (_b = (_a = this.skill) === null || _a === void 0 ? void 0 : _a.serialize()) !== null && _b !== void 0 ? _b : {};
|
|
57
65
|
const response = yield this.sendMessageToAdapter(model);
|
|
58
66
|
let parsedMessage;
|
|
@@ -80,7 +88,17 @@ class SprucebotLlmBotImpl extends AbstractEventEmitter {
|
|
|
80
88
|
});
|
|
81
89
|
cb === null || cb === void 0 ? void 0 : cb(parsedMessage);
|
|
82
90
|
if (callbackResults) {
|
|
83
|
-
|
|
91
|
+
let message;
|
|
92
|
+
if (typeof callbackResults === 'string') {
|
|
93
|
+
message = `API Results: ${callbackResults}`;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
message = {
|
|
97
|
+
imageBase64: callbackResults.imageBase64,
|
|
98
|
+
imageDescription: `API Results: ${callbackResults.imageDescription}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
yield this.sendMessage(message, cb);
|
|
84
102
|
}
|
|
85
103
|
return parsedMessage;
|
|
86
104
|
});
|
|
@@ -3,7 +3,6 @@ import { LlmAdapter, SendMessageOptions, SprucebotLlmBot } from '../../llm.types
|
|
|
3
3
|
export declare class OpenAiAdapter implements LlmAdapter {
|
|
4
4
|
static OpenAI: typeof OpenAI;
|
|
5
5
|
private api;
|
|
6
|
-
private log;
|
|
7
6
|
protected constructor(apiKey: string);
|
|
8
7
|
static Adapter(apiKey: string): OpenAiAdapter;
|
|
9
8
|
sendMessage(bot: SprucebotLlmBot, options?: SendMessageOptions): Promise<string>;
|
|
@@ -8,12 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { assertOptions } from '@sprucelabs/schema';
|
|
11
|
-
import { buildLog } from '@sprucelabs/spruce-skill-utils';
|
|
12
11
|
import OpenAI from 'openai';
|
|
13
12
|
import OpenAiMessageBuilder from './OpenAiMessageBuilder.js';
|
|
14
13
|
export class OpenAiAdapter {
|
|
14
|
+
// private log = buildLog('SprucebotLLM::OpenAiAdapter')
|
|
15
15
|
constructor(apiKey) {
|
|
16
|
-
this.log = buildLog('SprucebotLLM::OpenAiAdapter');
|
|
17
16
|
assertOptions({ apiKey }, ['apiKey']);
|
|
18
17
|
this.api = new OpenAiAdapter.OpenAI({ apiKey });
|
|
19
18
|
}
|
|
@@ -25,13 +24,16 @@ export class OpenAiAdapter {
|
|
|
25
24
|
var _a, _b, _c, _d, _e, _f;
|
|
26
25
|
const messageBuilder = OpenAiMessageBuilder.Builder(bot);
|
|
27
26
|
const messages = messageBuilder.buildMessages();
|
|
28
|
-
this.log.info(
|
|
27
|
+
// this.log.info(
|
|
28
|
+
// 'Sending message to OpenAI',
|
|
29
|
+
// JSON.stringify(messages, null, 2)
|
|
30
|
+
// )
|
|
29
31
|
const response = yield this.api.chat.completions.create({
|
|
30
32
|
messages,
|
|
31
33
|
model: (_a = options === null || options === void 0 ? void 0 : options.model) !== null && _a !== void 0 ? _a : 'gpt-4o',
|
|
32
34
|
});
|
|
33
35
|
const message = (_f = (_e = (_d = (_c = (_b = response.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : MESSAGE_RESPONSE_ERROR_MESSAGE;
|
|
34
|
-
this.log.info('Received response from OpenAI', message)
|
|
36
|
+
// this.log.info('Received response from OpenAI', message)
|
|
35
37
|
return message;
|
|
36
38
|
});
|
|
37
39
|
}
|
|
@@ -6,6 +6,7 @@ export default class OpenAiMessageBuilder {
|
|
|
6
6
|
static Builder(bot: SprucebotLlmBot): OpenAiMessageBuilder;
|
|
7
7
|
buildMessages(): ChatCompletionMessageParam[];
|
|
8
8
|
private buildChatHistoryMessages;
|
|
9
|
+
private mapMessageToCompletion;
|
|
9
10
|
private buildFirstMessage;
|
|
10
11
|
private buildSkillMessages;
|
|
11
12
|
private buildCallbacksMessage;
|
|
@@ -22,10 +22,28 @@ export default class OpenAiMessageBuilder {
|
|
|
22
22
|
if (limit > 0) {
|
|
23
23
|
messagesBeingConsidered = messages.slice(Math.max(messages.length - limit, 0));
|
|
24
24
|
}
|
|
25
|
-
return messagesBeingConsidered.map((message) => (
|
|
25
|
+
return messagesBeingConsidered.map((message) => this.mapMessageToCompletion(message));
|
|
26
|
+
}
|
|
27
|
+
mapMessageToCompletion(message) {
|
|
28
|
+
let content = message.message;
|
|
29
|
+
if (message.imageBase64) {
|
|
30
|
+
content = [
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: message.message,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'image_url',
|
|
37
|
+
image_url: {
|
|
38
|
+
url: `data:image/png;base64,${message.imageBase64}`,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
26
44
|
role: message.from === 'Me' ? 'user' : 'assistant',
|
|
27
|
-
content
|
|
28
|
-
}
|
|
45
|
+
content,
|
|
46
|
+
};
|
|
29
47
|
}
|
|
30
48
|
buildFirstMessage(youAre) {
|
|
31
49
|
return {
|
|
@@ -82,7 +100,7 @@ export default class OpenAiMessageBuilder {
|
|
|
82
100
|
const api = `<APIReference>\n\n${descriptions.join('\n\n')}</APIReference>`;
|
|
83
101
|
return {
|
|
84
102
|
role: 'system',
|
|
85
|
-
content: `You have an API available to you to lookup answers. When you need the response of the function call to proceed, you can call a function using a custom markup we created that looks like this: <<
|
|
103
|
+
content: `You have an API available to you to lookup answers. When you need the response of the function call to proceed, you can call a function using a custom markup we created that looks like this: <<FunctionName/>>. The API will respond with the results and then you can continue the conversation with your new knowledge. If the api call has parameters, call it like this: <<FunctionName>>{{parametersJsonEncoded}}<</FunctionName>>. Make sure to json encode the data and drop it between the function tags. The API is as follows (in xml format):\n\n${api}`,
|
|
86
104
|
};
|
|
87
105
|
}
|
|
88
106
|
buildPleaseKeepInMindMessage(pleaseKeepInMindThat) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import renderPlaceholder from '../parsingResponses/renderPlaceholder.js';
|
|
2
2
|
export const STATE_BOUNDARY = '*****';
|
|
3
3
|
export const DONE_TOKEN = `DONE_DONE_DONE`;
|
|
4
4
|
export const CALLBACK_BOUNDARY = 'xxxxx';
|
|
@@ -37,7 +37,7 @@ When asking me about a "select" field, make sure I only pick a valid choice by s
|
|
|
37
37
|
|
|
38
38
|
Your primary objective for this conversation is <%= it.skill.yourJobIfYouChooseToAcceptItIs %>
|
|
39
39
|
<% if (it.skill.callbacks) { %>
|
|
40
|
-
While we are talking, there are going to be things I don't want you to answer, but instead to respond with a placeholder in the form of ${
|
|
40
|
+
While we are talking, there are going to be things I don't want you to answer, but instead to respond with a placeholder in the form of ${renderPlaceholder('example')}.
|
|
41
41
|
|
|
42
42
|
Here are the placeholders we will be using:
|
|
43
43
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import dotenv from 'dotenv';
|
|
12
|
+
import { OpenAiAdapter } from './bots/adapters/OpenAi.js';
|
|
13
|
+
import SprucebotLlmFactory from './bots/SprucebotLlmFactory.js';
|
|
14
|
+
dotenv.config();
|
|
15
|
+
void (() => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
console.clear();
|
|
17
|
+
const image1 = fs.readFileSync('build/examples/images/image1.png');
|
|
18
|
+
const image2 = fs.readFileSync('build/examples/images/image2.png');
|
|
19
|
+
const base64Image1 = image1.toString('base64');
|
|
20
|
+
const base64Image2 = image2.toString('base64');
|
|
21
|
+
const adapter = OpenAiAdapter.Adapter(process.env.OPEN_AI_API_KEY);
|
|
22
|
+
const bots = SprucebotLlmFactory.Factory(adapter);
|
|
23
|
+
const skill = bots.Skill({
|
|
24
|
+
weAreDoneWhen: 'you have described both images to me',
|
|
25
|
+
yourJobIfYouChooseToAcceptItIs: 'to review the image i send you, invoke the getNextImage function and then describe that image too.',
|
|
26
|
+
callbacks: {
|
|
27
|
+
getNextImage: {
|
|
28
|
+
cb: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
return {
|
|
30
|
+
imageBase64: base64Image2,
|
|
31
|
+
imageDescription: 'A beautiful sunset over the mountains.',
|
|
32
|
+
};
|
|
33
|
+
}),
|
|
34
|
+
useThisWhenever: 'you want to get the next image',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const bot = bots.Bot({
|
|
39
|
+
skill,
|
|
40
|
+
youAre: "a bot named Sprucebot that is in test mode. At the start of every conversation, you introduce yourself and announce that you are in test mode so I don't get confused! You are both hip and adorable. You say things like, 'Jeepers' and 'Golly' or even 'Jeezey peezy'!",
|
|
41
|
+
});
|
|
42
|
+
yield bot.sendMessage({
|
|
43
|
+
imageBase64: base64Image1,
|
|
44
|
+
imageDescription: 'The first image',
|
|
45
|
+
}, (message) => console.log('>', message));
|
|
46
|
+
}))();
|
package/build/esm/llm.types.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface BotOptions<StateSchema extends Schema = Schema, State extends S
|
|
|
8
8
|
export interface SprucebotLlmBot<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends MercuryEventEmitter<LlmEventContract> {
|
|
9
9
|
markAsDone(): void;
|
|
10
10
|
getIsDone(): boolean;
|
|
11
|
-
sendMessage(message:
|
|
11
|
+
sendMessage(message: SendMessage, responseCb?: MessageResponseCallback): Promise<string>;
|
|
12
12
|
serialize(): SerializedBot<StateSchema, State>;
|
|
13
13
|
updateState(state: Partial<State>): Promise<void>;
|
|
14
14
|
setSkill(skill: SprucebotLLmSkill<any>): void;
|
|
@@ -41,6 +41,7 @@ export type LlmEventContract = typeof llmEventContract;
|
|
|
41
41
|
export interface LlmMessage {
|
|
42
42
|
from: 'Me' | 'You';
|
|
43
43
|
message: string;
|
|
44
|
+
imageBase64?: string;
|
|
44
45
|
}
|
|
45
46
|
export interface SkillOptions<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> {
|
|
46
47
|
yourJobIfYouChooseToAcceptItIs: string;
|
|
@@ -60,7 +61,7 @@ export interface SerializedSkill<StateSchema extends Schema = Schema, State exte
|
|
|
60
61
|
}
|
|
61
62
|
export type LlmCallbackMap = Record<string, LlmCallback>;
|
|
62
63
|
export interface LlmCallback {
|
|
63
|
-
cb: (options?: Record<string, any>) =>
|
|
64
|
+
cb: (options?: Record<string, any>) => SendMessage | Promise<SendMessage>;
|
|
64
65
|
useThisWhenever: string;
|
|
65
66
|
parameters?: LlmCallbackParameter[];
|
|
66
67
|
}
|
|
@@ -71,3 +72,8 @@ export interface LlmCallbackParameter {
|
|
|
71
72
|
description?: string;
|
|
72
73
|
}
|
|
73
74
|
export type MessageResponseCallback = (message: string) => any;
|
|
75
|
+
export type SendMessage = string | SendMessageWithImage;
|
|
76
|
+
export interface SendMessageWithImage {
|
|
77
|
+
imageDescription: string;
|
|
78
|
+
imageBase64: string;
|
|
79
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LlmCallbackMap } from '../llm.types';
|
|
1
|
+
import { LlmCallbackMap, SendMessage } from '../llm.types';
|
|
2
2
|
export default class ResponseParser {
|
|
3
3
|
private static instance;
|
|
4
4
|
static setInstance(parser: ResponseParser): void;
|
|
@@ -13,5 +13,5 @@ export interface ParsedResponse {
|
|
|
13
13
|
isDone: boolean;
|
|
14
14
|
state?: Record<string, any>;
|
|
15
15
|
message: string;
|
|
16
|
-
callbackResults?:
|
|
16
|
+
callbackResults?: SendMessage;
|
|
17
17
|
}
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { DONE_TOKEN, STATE_BOUNDARY } from '../bots/templates.js';
|
|
11
11
|
import SpruceError from '../errors/SpruceError.js';
|
|
12
|
-
import
|
|
12
|
+
import renderPlaceholder from './renderPlaceholder.js';
|
|
13
13
|
class ResponseParser {
|
|
14
14
|
static setInstance(parser) {
|
|
15
15
|
this.instance = parser;
|
|
@@ -24,7 +24,7 @@ class ResponseParser {
|
|
|
24
24
|
let state;
|
|
25
25
|
let callbackResults;
|
|
26
26
|
for (const key of Object.keys(callbacks || {})) {
|
|
27
|
-
const match = message.match(
|
|
27
|
+
const match = message.match(renderPlaceholder(key));
|
|
28
28
|
if (match) {
|
|
29
29
|
message = yield this.invokeCallbackAndDropInLegacyResults(callbacks, key, message);
|
|
30
30
|
}
|
|
@@ -74,8 +74,8 @@ class ResponseParser {
|
|
|
74
74
|
invokeCallbackAndDropInLegacyResults(callbacks, key, message) {
|
|
75
75
|
return __awaiter(this, void 0, void 0, function* () {
|
|
76
76
|
var _a;
|
|
77
|
-
const v = yield ((_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks[key]) === null || _a === void 0 ? void 0 : _a.cb());
|
|
78
|
-
message = message.replace(
|
|
77
|
+
const v = (yield ((_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks[key]) === null || _a === void 0 ? void 0 : _a.cb()));
|
|
78
|
+
message = message.replace(renderPlaceholder(key), v !== null && v !== void 0 ? v : '').trim();
|
|
79
79
|
return message;
|
|
80
80
|
});
|
|
81
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function
|
|
1
|
+
export default function renderPlaceholder(key: string): string;
|
|
Binary file
|
|
Binary file
|
package/build/llm.types.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface BotOptions<StateSchema extends Schema = Schema, State extends S
|
|
|
8
8
|
export interface SprucebotLlmBot<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends MercuryEventEmitter<LlmEventContract> {
|
|
9
9
|
markAsDone(): void;
|
|
10
10
|
getIsDone(): boolean;
|
|
11
|
-
sendMessage(message:
|
|
11
|
+
sendMessage(message: SendMessage, responseCb?: MessageResponseCallback): Promise<string>;
|
|
12
12
|
serialize(): SerializedBot<StateSchema, State>;
|
|
13
13
|
updateState(state: Partial<State>): Promise<void>;
|
|
14
14
|
setSkill(skill: SprucebotLLmSkill<any>): void;
|
|
@@ -41,6 +41,7 @@ export type LlmEventContract = typeof llmEventContract;
|
|
|
41
41
|
export interface LlmMessage {
|
|
42
42
|
from: 'Me' | 'You';
|
|
43
43
|
message: string;
|
|
44
|
+
imageBase64?: string;
|
|
44
45
|
}
|
|
45
46
|
export interface SkillOptions<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> {
|
|
46
47
|
yourJobIfYouChooseToAcceptItIs: string;
|
|
@@ -60,7 +61,7 @@ export interface SerializedSkill<StateSchema extends Schema = Schema, State exte
|
|
|
60
61
|
}
|
|
61
62
|
export type LlmCallbackMap = Record<string, LlmCallback>;
|
|
62
63
|
export interface LlmCallback {
|
|
63
|
-
cb: (options?: Record<string, any>) =>
|
|
64
|
+
cb: (options?: Record<string, any>) => SendMessage | Promise<SendMessage>;
|
|
64
65
|
useThisWhenever: string;
|
|
65
66
|
parameters?: LlmCallbackParameter[];
|
|
66
67
|
}
|
|
@@ -71,3 +72,8 @@ export interface LlmCallbackParameter {
|
|
|
71
72
|
description?: string;
|
|
72
73
|
}
|
|
73
74
|
export type MessageResponseCallback = (message: string) => any;
|
|
75
|
+
export type SendMessage = string | SendMessageWithImage;
|
|
76
|
+
export interface SendMessageWithImage {
|
|
77
|
+
imageDescription: string;
|
|
78
|
+
imageBase64: string;
|
|
79
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LlmCallbackMap } from '../llm.types';
|
|
1
|
+
import { LlmCallbackMap, SendMessage } from '../llm.types';
|
|
2
2
|
export default class ResponseParser {
|
|
3
3
|
private static instance;
|
|
4
4
|
static setInstance(parser: ResponseParser): void;
|
|
@@ -13,5 +13,5 @@ export interface ParsedResponse {
|
|
|
13
13
|
isDone: boolean;
|
|
14
14
|
state?: Record<string, any>;
|
|
15
15
|
message: string;
|
|
16
|
-
callbackResults?:
|
|
16
|
+
callbackResults?: SendMessage;
|
|
17
17
|
}
|
|
@@ -65,7 +65,7 @@ class ResponseParser {
|
|
|
65
65
|
return matchedCallback;
|
|
66
66
|
}
|
|
67
67
|
async invokeCallbackAndDropInLegacyResults(callbacks, key, message) {
|
|
68
|
-
const v = await callbacks?.[key]?.cb();
|
|
68
|
+
const v = (await callbacks?.[key]?.cb());
|
|
69
69
|
message = message.replace((0, renderPlaceholder_1.default)(key), v ?? '').trim();
|
|
70
70
|
return message;
|
|
71
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function
|
|
1
|
+
export default function renderPlaceholder(key: string): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default =
|
|
3
|
+
exports.default = renderPlaceholder;
|
|
4
4
|
const templates_1 = require("../bots/templates");
|
|
5
|
-
function
|
|
5
|
+
function renderPlaceholder(key) {
|
|
6
6
|
return `${templates_1.CALLBACK_BOUNDARY} ${key} ${templates_1.CALLBACK_BOUNDARY}`;
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"@sprucelabs/spruce-test-fixtures"
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
|
-
"version": "8.
|
|
12
|
+
"version": "8.5.1",
|
|
13
13
|
"files": [
|
|
14
14
|
"build"
|
|
15
15
|
],
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"watch.build.dev": "tsc-watch --sourceMap --onCompilationComplete 'yarn run post.watch.build'",
|
|
51
51
|
"watch.rebuild": "yarn run clean.all && yarn install && yarn run watch.build.dev",
|
|
52
52
|
"watch.tsc": "tsc -w",
|
|
53
|
-
"chat": "node ./build/chat.js"
|
|
53
|
+
"chat": "node ./build/chat.js",
|
|
54
|
+
"chat.images": "node ./build/chatWithImages.js"
|
|
54
55
|
},
|
|
55
56
|
"dependencies": {
|
|
56
57
|
"@sprucelabs/error": "^6.0.598",
|