From a7f7add26a403cb065a828123b016958124ae902 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:15:27 +0200
Subject: [PATCH 01/35] style: add quote props rule
---
eslint.config.js | 1 +
prettier.config.js | 1 +
2 files changed, 2 insertions(+)
diff --git a/eslint.config.js b/eslint.config.js
index b3df902..5e73ec5 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -66,6 +66,7 @@ export default tseslint.config(
],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-object-type": "off",
+ "quote-props": ["error", "consistent"],
},
},
{
diff --git a/prettier.config.js b/prettier.config.js
index ae16854..6d974cd 100644
--- a/prettier.config.js
+++ b/prettier.config.js
@@ -3,6 +3,7 @@
/** @type {import("prettier").Config} */
const config = {
printWidth: 100,
+ quoteProps: "consistent",
};
export default config;
From bd4420c27b25a9f001456fc443477ff4f7ad9e0a Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:15:40 +0200
Subject: [PATCH 02/35] test: print console traces
---
vitest.config.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/vitest.config.ts b/vitest.config.ts
index 50e3fdc..eaf7f3d 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -7,6 +7,7 @@ export default defineConfig({
globals: true,
passWithNoTests: true,
testTimeout: 120 * 1000,
+ printConsoleTrace: true,
setupFiles: ["./tests/setup.ts"],
deps: {
interopDefault: false,
From f4797b80ad2712444c15f3aa050f2f2db8c1d86e Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:16:36 +0200
Subject: [PATCH 03/35] feat(watsonx): add chat preset
---
examples/llms/providers/watsonx.ts | 35 +-----
examples/llms/providers/watsonx_verbose.ts | 55 ++++++++
src/adapters/shared/llmChatTemplates.ts | 139 +++++++++++++++++++++
src/adapters/watsonx/chat.ts | 34 ++++-
src/adapters/watsonx/chatPreset.ts | 70 +++++++++++
5 files changed, 299 insertions(+), 34 deletions(-)
create mode 100644 examples/llms/providers/watsonx_verbose.ts
create mode 100644 src/adapters/shared/llmChatTemplates.ts
create mode 100644 src/adapters/watsonx/chatPreset.ts
diff --git a/examples/llms/providers/watsonx.ts b/examples/llms/providers/watsonx.ts
index 6971ada..da37e57 100644
--- a/examples/llms/providers/watsonx.ts
+++ b/examples/llms/providers/watsonx.ts
@@ -1,47 +1,16 @@
import "dotenv/config";
import { BaseMessage } from "@/llms/primitives/message.js";
import { WatsonXChatLLM } from "@/adapters/watsonx/chat.js";
-import { WatsonXLLM } from "@/adapters/watsonx/llm.js";
-import { PromptTemplate } from "@/template.js";
-const template = new PromptTemplate({
- variables: ["messages"],
- template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
-
-{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
-
-{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
-
-{{assistant}}<|eot_id|>{{/assistant}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
-
-`,
-});
-
-const llm = new WatsonXLLM({
- modelId: "meta-llama/llama-3-70b-instruct",
- projectId: process.env.WATSONX_PROJECT_ID,
+const chatLLM = WatsonXChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct", {
apiKey: process.env.WATSONX_API_KEY,
+ projectId: process.env.WATSONX_PROJECT_ID,
parameters: {
decoding_method: "greedy",
max_new_tokens: 50,
},
});
-const chatLLM = new WatsonXChatLLM({
- llm,
- config: {
- messagesToPrompt(messages: BaseMessage[]) {
- return template.render({
- messages: messages.map((message) => ({
- system: message.role === "system" ? [message.text] : [],
- user: message.role === "user" ? [message.text] : [],
- assistant: message.role === "assistant" ? [message.text] : [],
- })),
- });
- },
- },
-});
-
console.info("Meta", await chatLLM.meta());
const response = await chatLLM.generate([
diff --git a/examples/llms/providers/watsonx_verbose.ts b/examples/llms/providers/watsonx_verbose.ts
new file mode 100644
index 0000000..6ebff6c
--- /dev/null
+++ b/examples/llms/providers/watsonx_verbose.ts
@@ -0,0 +1,55 @@
+import "dotenv/config";
+import { BaseMessage } from "@/llms/primitives/message.js";
+import { WatsonXChatLLM } from "@/adapters/watsonx/chat.js";
+import { WatsonXLLM } from "@/adapters/watsonx/llm.js";
+import { PromptTemplate } from "@/template.js";
+
+const template = new PromptTemplate({
+ variables: ["messages"],
+ template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
+
+{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
+
+{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
+
+{{assistant}}<|eot_id|>{{/assistant}}{{#ipython}}<|start_header_id|>ipython<|end_header_id|>
+
+{{ipython}}<|eot_id|>{{/ipython}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
+`,
+});
+
+const llm = new WatsonXLLM({
+ modelId: "meta-llama/llama-3-1-70b-instruct",
+ projectId: process.env.WATSONX_PROJECT_ID,
+ apiKey: process.env.WATSONX_API_KEY,
+ parameters: {
+ decoding_method: "greedy",
+ max_new_tokens: 50,
+ },
+});
+
+const chatLLM = new WatsonXChatLLM({
+ llm,
+ config: {
+ messagesToPrompt(messages: BaseMessage[]) {
+ return template.render({
+ messages: messages.map((message) => ({
+ system: message.role === "system" ? [message.text] : [],
+ user: message.role === "user" ? [message.text] : [],
+ assistant: message.role === "assistant" ? [message.text] : [],
+ ipython: message.role === "ipython" ? [message.text] : [],
+ })),
+ });
+ },
+ },
+});
+
+console.info("Meta", await chatLLM.meta());
+
+const response = await chatLLM.generate([
+ BaseMessage.of({
+ role: "user",
+ text: "Hello world!",
+ }),
+]);
+console.info(response.messages[0]);
diff --git a/src/adapters/shared/llmChatTemplates.ts b/src/adapters/shared/llmChatTemplates.ts
new file mode 100644
index 0000000..0e48eef
--- /dev/null
+++ b/src/adapters/shared/llmChatTemplates.ts
@@ -0,0 +1,139 @@
+/**
+ * Copyright 2024 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { PromptTemplate } from "@/template.js";
+import { BaseMessage } from "@/llms/primitives/message.js";
+import { ValueError } from "@/errors.js";
+import { isDefined, mapValues, pickBy } from "remeda";
+
+export interface Template {
+ template: PromptTemplate<"messages">;
+ messagesToPrompt: (template: PromptTemplate<"messages">) => (messages: BaseMessage[]) => string;
+ parameters: {
+ stop_sequence: string[];
+ };
+}
+
+function messagesToPromptFactory(rolesOverride: Record = {}) {
+ const roles: Record = pickBy(
+ {
+ system: "system",
+ user: "user",
+ assistant: "assistant",
+ ...rolesOverride,
+ },
+ isDefined,
+ );
+
+ return (template: PromptTemplate<"messages">) => {
+ return (messages: BaseMessage[]) => {
+ return template.render({
+ messages: messages.map((message) =>
+ mapValues(roles, (role) => (message.role === role ? [message.text] : [])),
+ ),
+ });
+ };
+ };
+}
+
+const llama31: Template = {
+ template: new PromptTemplate({
+ variables: ["messages"],
+ template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
+
+{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
+
+{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
+
+{{assistant}}<|eot_id|>{{/assistant}}{{#ipython}}<|start_header_id|>ipython<|end_header_id|>
+
+{{ipython}}<|eot_id|>{{/ipython}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
+`,
+ }),
+ messagesToPrompt: messagesToPromptFactory({ ipython: "ipython" }),
+ parameters: {
+ stop_sequence: ["<|eot_id|>"],
+ },
+};
+
+const llama3: Template = {
+ template: new PromptTemplate({
+ variables: ["messages"],
+ template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
+
+{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
+
+{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
+
+{{assistant}}<|eot_id|>{{/assistant}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
+`,
+ }),
+ messagesToPrompt: messagesToPromptFactory(),
+ parameters: {
+ stop_sequence: ["<|eot_id|>"],
+ },
+};
+
+const qwen2: Template = {
+ template: new PromptTemplate({
+ variables: ["messages"],
+ template: `{{#messages}}{{#system}}<|im_start|>system
+{{system}}<|im_end|>
+{{ end }}{{/system}}{{#user}}<|im_start|>user
+{{user}}<|im_end|>
+{{ end }}{{/user}}{{#assistant}}<|im_start|>assistant
+{{assistant}}<|im_end|>
+{{ end }}{{/assistant}}{{/messages}}<|im_start|>assistant
+`,
+ }),
+ messagesToPrompt: messagesToPromptFactory(),
+ parameters: {
+ stop_sequence: ["<|im_end|>"],
+ },
+};
+
+export class LLMChatTemplates {
+ protected static readonly registry = {
+ "llama3.1": llama31,
+ "llama3": llama3,
+ "qwen2": qwen2,
+ };
+
+ static register(model: string, template: Template, override = false) {
+ if (model in this.registry && !override) {
+ throw new ValueError(`Template for model '${model}' already exists!`);
+ }
+ this.registry[model as keyof typeof this.registry] = template;
+ }
+
+ static has(model: string): boolean {
+ return Boolean(model && model in this.registry);
+ }
+
+ static get(model: keyof typeof LLMChatTemplates.registry): Template;
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ static get(model: string): Template;
+ static get(model: string): Template {
+ if (!this.has(model)) {
+ throw new ValueError(`Template for model '${model}' not found!`, [], {
+ context: {
+ validModels: Object.keys(this.registry),
+ },
+ });
+ }
+ return this.registry[model as keyof typeof this.registry];
+ }
+}
diff --git a/src/adapters/watsonx/chat.ts b/src/adapters/watsonx/chat.ts
index 1fe24e7..1fcf70b 100644
--- a/src/adapters/watsonx/chat.ts
+++ b/src/adapters/watsonx/chat.ts
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-import { AsyncStream, GenerateCallbacks } from "@/llms/base.js";
+import { AsyncStream, GenerateCallbacks, LLMError } from "@/llms/base.js";
import {
WatsonXLLM,
WatsonXLLMGenerateOptions,
WatsonXLLMParameters,
WatsonXLLMOutput,
+ WatsonXLLMInput,
} from "@/adapters/watsonx/llm.js";
import { ChatLLM, ChatLLMOutput } from "@/llms/chat.js";
import { BaseMessage, Role } from "@/llms/primitives/message.js";
@@ -29,6 +30,8 @@ import { transformAsyncIterable } from "@/internals/helpers/stream.js";
import { shallowCopy } from "@/serializer/utils.js";
import { Emitter } from "@/emitter/emitter.js";
import { GetRunContext } from "@/context.js";
+import { isFunction, isObjectType } from "remeda";
+import { WatsonXChatLLMPreset, WatsonXChatLLMPresetModel } from "@/adapters/watsonx/chatPreset.js";
export class WatsonXChatLLMOutput extends ChatLLMOutput {
public readonly raw: WatsonXLLMOutput;
@@ -158,4 +161,33 @@ export class WatsonXChatLLM extends ChatLLM & {
+ parameters?: WatsonXLLMParameters | ((value: WatsonXLLMParameters) => WatsonXLLMParameters);
+ },
+ ) {
+ const preset = WatsonXChatLLMPreset[modelId]?.();
+ if (!preset) {
+ throw new LLMError(`Model "${modelId}" does not exist in preset.`);
+ }
+
+ let parameters = preset.base.parameters ?? {};
+ if (isFunction(overrides?.parameters)) {
+ parameters = overrides?.parameters(parameters);
+ } else if (isObjectType(overrides?.parameters)) {
+ parameters = overrides?.parameters;
+ }
+
+ return new WatsonXChatLLM({
+ config: preset.chat,
+ llm: new WatsonXLLM({
+ ...preset.base,
+ ...overrides,
+ parameters,
+ modelId,
+ }),
+ });
+ }
}
diff --git a/src/adapters/watsonx/chatPreset.ts b/src/adapters/watsonx/chatPreset.ts
new file mode 100644
index 0000000..ab46c99
--- /dev/null
+++ b/src/adapters/watsonx/chatPreset.ts
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2024 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { LLMChatTemplates } from "@/adapters/shared/llmChatTemplates.js";
+import { WatsonXLLMInput } from "@/adapters/watsonx/llm.js";
+import { WatsonXChatLLMInputConfig } from "@/adapters/watsonx/chat.js";
+
+interface WatsonXChatLLMPreset {
+ chat: WatsonXChatLLMInputConfig;
+ base: Omit;
+}
+
+export const WatsonXChatLLMPreset = {
+ "meta-llama/llama-3-1-70b-instruct": (): WatsonXChatLLMPreset => {
+ const { template, messagesToPrompt, parameters } = LLMChatTemplates.get("llama3.1");
+
+ return {
+ base: {
+ parameters: {
+ decoding_method: "greedy",
+ include_stop_sequence: false,
+ max_new_tokens: 2048,
+ repetition_penalty: 1.03,
+ stop_sequences: [...parameters.stop_sequence],
+ },
+ },
+ chat: {
+ messagesToPrompt: messagesToPrompt(template),
+ },
+ };
+ },
+ "meta-llama/llama-3-1-8b-instruct"() {
+ return this["meta-llama/llama-3-1-70b-instruct"]();
+ },
+ "meta-llama/llama-3-70b-instruct": (): WatsonXChatLLMPreset => {
+ const { template, messagesToPrompt, parameters } = LLMChatTemplates.get("llama3");
+
+ return {
+ base: {
+ parameters: {
+ decoding_method: "greedy",
+ max_new_tokens: 1500,
+ include_stop_sequence: false,
+ stop_sequences: [...parameters.stop_sequence],
+ },
+ },
+ chat: {
+ messagesToPrompt: messagesToPrompt(template),
+ },
+ };
+ },
+ "meta-llama/llama-3-8b-instruct"() {
+ return this["meta-llama/llama-3-70b-instruct"]();
+ },
+} as const;
+
+export type WatsonXChatLLMPresetModel = keyof typeof WatsonXChatLLMPreset;
From 369da929281932ae6151eb388601370c83c113b5 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:17:06 +0200
Subject: [PATCH 04/35] feat(watsonx): auto load accessToken/apiKey from env
---
src/adapters/watsonx/llm.ts | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/adapters/watsonx/llm.ts b/src/adapters/watsonx/llm.ts
index 2f26232..e087ffc 100644
--- a/src/adapters/watsonx/llm.ts
+++ b/src/adapters/watsonx/llm.ts
@@ -29,7 +29,7 @@ import {
} from "@/llms/base.js";
import { HttpError } from "@ibm-generative-ai/node-sdk";
import * as R from "remeda";
-import { FrameworkError } from "@/errors.js";
+import { FrameworkError, ValueError } from "@/errors.js";
import { Cache, CacheFn } from "@/cache/decoratorCache.js";
import { shallowCopy } from "@/serializer/utils.js";
import { safeSum } from "@/internals/helpers/number.js";
@@ -38,6 +38,7 @@ import { createURLParams, RestfulClient, RestfulClientError } from "@/internals/
import { identity } from "remeda";
import { Emitter } from "@/emitter/emitter.js";
import { GetRunContext } from "@/context.js";
+import { getEnv } from "@/internals/env.js";
export interface WatsonXLLMOutputMeta {
model_id: string;
@@ -183,6 +184,17 @@ function createApiClient({
};
})();
+ accessToken = accessToken || getEnv("WATSONX_ACCESS_TOKEN");
+ apiKey = apiKey || getEnv("WATSONX_API_KEY");
+ if (!accessToken && !apiKey) {
+ throw new ValueError(
+ [
+ `Neither "accessToken" nor "apiKey" has been provided.`,
+ `Either set them directly or put them in ENV ("WATSONX_ACCESS_TOKEN" / "WATSONX_API_KEY")`,
+ ].join("\n"),
+ );
+ }
+
const getHeaders = CacheFn.create(async () => {
const getAccessToken = async () => {
if (accessToken) {
@@ -216,8 +228,8 @@ function createApiClient({
const response = await getAccessToken();
getHeaders.updateTTL(response.ttl);
return new Headers({
- Authorization: `Bearer ${response.token}`,
- Accept: "application/json",
+ "Authorization": `Bearer ${response.token}`,
+ "Accept": "application/json",
"Content-Type": "application/json",
});
});
From 5f5fa05ad502fd1e140f4e0c3048e0d629d0f16d Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:17:57 +0200
Subject: [PATCH 05/35] feat(bam): refactor chat preset
---
src/adapters/bam/chatPreset.ts | 93 +++++-----------------------------
1 file changed, 13 insertions(+), 80 deletions(-)
diff --git a/src/adapters/bam/chatPreset.ts b/src/adapters/bam/chatPreset.ts
index a8bfe9a..278b2b9 100644
--- a/src/adapters/bam/chatPreset.ts
+++ b/src/adapters/bam/chatPreset.ts
@@ -15,10 +15,8 @@
*/
import { BAMChatLLMInputConfig } from "@/adapters/bam/chat.js";
-import { BaseMessage } from "@/llms/primitives/message.js";
-import { PromptTemplate } from "@/template.js";
import { BAMLLMInput } from "@/adapters/bam/llm.js";
-import { toBoundedFunction } from "@/serializer/utils.js";
+import { LLMChatTemplates } from "@/adapters/shared/llmChatTemplates.js";
interface BAMChatLLMPreset {
chat: BAMChatLLMInputConfig;
@@ -27,6 +25,8 @@ interface BAMChatLLMPreset {
export const BAMChatLLMPreset = {
"meta-llama/llama-3-1-70b-instruct": (): BAMChatLLMPreset => {
+ const { template, parameters, messagesToPrompt } = LLMChatTemplates.get("llama3.1");
+
return {
base: {
parameters: {
@@ -34,111 +34,44 @@ export const BAMChatLLMPreset = {
include_stop_sequence: false,
max_new_tokens: 2048,
repetition_penalty: 1.03,
- stop_sequences: ["<|eot_id|>"],
+ stop_sequences: [...parameters.stop_sequence],
},
},
chat: {
- messagesToPrompt: toBoundedFunction(
- (messages: BaseMessage[]) => {
- const template = new PromptTemplate({
- variables: ["messages"],
- template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
-
-{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
-
-{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
-
-{{assistant}}<|eot_id|>{{/assistant}}{{#ipython}}<|start_header_id|>ipython<|end_header_id|>
-
-{{ipython}}<|eot_id|>{{/ipython}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
-`,
- });
- return template.render({
- messages: messages.map((message) => ({
- system: message.role === "system" ? [message.text] : [],
- user: message.role === "user" ? [message.text] : [],
- assistant: message.role === "assistant" ? [message.text] : [],
- ipython: message.role === "ipython" ? [message.text] : [],
- })),
- });
- },
- [PromptTemplate],
- ),
+ messagesToPrompt: messagesToPrompt(template),
},
};
},
"meta-llama/llama-3-70b-instruct": (): BAMChatLLMPreset => {
+ const { template, parameters, messagesToPrompt } = LLMChatTemplates.get("llama3");
+
return {
base: {
parameters: {
decoding_method: "greedy",
max_new_tokens: 1500,
include_stop_sequence: false,
- stop_sequences: ["<|eot_id|>"],
+ stop_sequences: [...parameters.stop_sequence],
},
},
chat: {
- messagesToPrompt: toBoundedFunction(
- (messages: BaseMessage[]) => {
- const template = new PromptTemplate({
- variables: ["messages"],
- template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
-
-{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
-
-{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
-
-{{assistant}}<|eot_id|>{{/assistant}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
-`,
- });
-
- return template.render({
- messages: messages.map((message) => ({
- system: message.role === "system" ? [message.text] : [],
- user: message.role === "user" ? [message.text] : [],
- assistant: message.role === "assistant" ? [message.text] : [],
- })),
- });
- },
- [PromptTemplate],
- ),
+ messagesToPrompt: messagesToPrompt(template),
},
};
},
"qwen/qwen2-72b-instruct": (): BAMChatLLMPreset => {
+ const { template, parameters, messagesToPrompt } = LLMChatTemplates.get("qwen2");
+
return {
base: {
parameters: {
decoding_method: "greedy",
include_stop_sequence: false,
- stop_sequences: ["<|im_end|>"],
+ stop_sequences: [...parameters.stop_sequence],
},
},
chat: {
- messagesToPrompt: toBoundedFunction(
- (messages: BaseMessage[]) => {
- const template = new PromptTemplate({
- variables: ["messages"],
- template: `{{#messages}}{{#system}}<|im_start|>system
-{{system}}<|im_end|>
-{{ end }}{{/system}}{{#user}}<|im_start|>user
-{{user}}<|im_end|>
-{{ end }}{{/user}}{{#assistant}}<|im_start|>assistant
-{{assistant}}<|im_end|>
-{{ end }}{{/assistant}}{{/messages}}<|im_start|>assistant
-`,
- });
-
- return template.render({
- messages: messages.map((message) => ({
- system: message.role === "system" ? [message.text] : [],
- user: message.role === "user" ? [message.text] : [],
- assistant: message.role === "assistant" ? [message.text] : [],
- })),
- });
- },
- [PromptTemplate],
- ),
+ messagesToPrompt: messagesToPrompt(template),
},
};
},
From 5c3357a890878daa29823d79aac274ba6d11a829 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:18:36 +0200
Subject: [PATCH 06/35] test: refactor tests
---
tests/e2e/adapters/sdk/chat.test.ts | 2 +-
tests/e2e/adapters/watsonx/chat.test.ts | 43 ++++---------------------
tests/e2e/agents/bee.test.ts | 7 ++--
tests/utils/llmFactory.ts | 33 +++++++++++++++++++
4 files changed, 43 insertions(+), 42 deletions(-)
create mode 100644 tests/utils/llmFactory.ts
diff --git a/tests/e2e/adapters/sdk/chat.test.ts b/tests/e2e/adapters/sdk/chat.test.ts
index 1a5b20b..f02ff6e 100644
--- a/tests/e2e/adapters/sdk/chat.test.ts
+++ b/tests/e2e/adapters/sdk/chat.test.ts
@@ -19,7 +19,7 @@ import { BaseMessage } from "@/llms/primitives/message.js";
import { expect } from "vitest";
import { verifyDeserialization } from "@tests/e2e/utils.js";
-describe("Adapter SDK Chat LLM", () => {
+describe.runIf(Boolean(process.env.GENAI_API_KEY))("Adapter SDK Chat LLM", () => {
const createChatLLM = () => {
return BAMChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct");
};
diff --git a/tests/e2e/adapters/watsonx/chat.test.ts b/tests/e2e/adapters/watsonx/chat.test.ts
index bf31a5a..d225288 100644
--- a/tests/e2e/adapters/watsonx/chat.test.ts
+++ b/tests/e2e/adapters/watsonx/chat.test.ts
@@ -14,52 +14,23 @@
* limitations under the License.
*/
-import { PromptTemplate } from "@/template.js";
import { BaseMessage } from "@/llms/primitives/message.js";
import { expect } from "vitest";
import { verifyDeserialization } from "@tests/e2e/utils.js";
import { WatsonXChatLLM } from "@/adapters/watsonx/chat.js";
-import { WatsonXLLM } from "@/adapters/watsonx/llm.js";
const apiKey = process.env.WATSONX_API_KEY!;
const projectId = process.env.WATSONX_PROJECT_ID!;
describe.runIf(Boolean(apiKey && projectId))("WatsonX Chat LLM", () => {
const createChatLLM = () => {
- const template = new PromptTemplate({
- variables: ["messages"],
- template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
-
-{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
-
-{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
-
-{{assistant}}<|eot_id|>{{/assistant}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
-
-`,
- });
-
- return new WatsonXChatLLM({
- llm: new WatsonXLLM({
- modelId: "meta-llama/llama-3-70b-instruct",
- projectId,
- apiKey,
- parameters: {
- decoding_method: "greedy",
- min_new_tokens: 5,
- max_new_tokens: 50,
- },
- }),
- config: {
- messagesToPrompt(messages: BaseMessage[]) {
- return template.render({
- messages: messages.map((message) => ({
- system: message.role === "system" ? [message.text] : [],
- user: message.role === "user" ? [message.text] : [],
- assistant: message.role === "assistant" ? [message.text] : [],
- })),
- });
- },
+ return WatsonXChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct", {
+ apiKey,
+ projectId,
+ parameters: {
+ decoding_method: "greedy",
+ min_new_tokens: 5,
+ max_new_tokens: 50,
},
});
};
diff --git a/tests/e2e/agents/bee.test.ts b/tests/e2e/agents/bee.test.ts
index ab086bf..a582546 100644
--- a/tests/e2e/agents/bee.test.ts
+++ b/tests/e2e/agents/bee.test.ts
@@ -19,17 +19,14 @@ import { FrameworkError } from "@/errors.js";
import { beforeEach, expect, vi } from "vitest";
import { Logger } from "@/logger/logger.js";
import { BeeAgent } from "@/agents/bee/agent.js";
-import { BAMChatLLM } from "@/adapters/bam/chat.js";
import { UnconstrainedMemory } from "@/memory/unconstrainedMemory.js";
import { BaseMessage } from "@/llms/primitives/message.js";
import { createCallbackRegister } from "@tests/e2e/utils.js";
import { omitEmptyValues } from "@/internals/helpers/object.js";
+import * as process from "node:process";
+import { createChatLLM } from "@tests/utils/llmFactory.js";
describe("Bee Agent", () => {
- const createChatLLM = () => {
- return BAMChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct");
- };
-
const createAgent = () => {
return new BeeAgent({
llm: createChatLLM(),
diff --git a/tests/utils/llmFactory.ts b/tests/utils/llmFactory.ts
new file mode 100644
index 0000000..4b207b6
--- /dev/null
+++ b/tests/utils/llmFactory.ts
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ChatLLM, ChatLLMOutput } from "@/llms/chat.js";
+import process from "node:process";
+import { BAMChatLLM } from "@/adapters/bam/chat.js";
+import { OpenAIChatLLM } from "@/adapters/openai/chat.js";
+import { WatsonXChatLLM } from "@/adapters/watsonx/chat.js";
+
+export function createChatLLM(): ChatLLM {
+ if (process.env.GENAI_API_KEY) {
+ return BAMChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct");
+ } else if (process.env.OPENAI_API_KEY) {
+ return new OpenAIChatLLM({ modelId: "gpt-4o" });
+ } else if (process.env.WATSONX_API_KEY) {
+ return WatsonXChatLLM.fromPreset("meta-llama/llama-3-70b-instruct");
+ } else {
+ throw new Error("No API key for any LLM provider has been provided. Cannot run test case.");
+ }
+}
From 63872cd3b07d4b5ab5690ca125e0d69b3f56e252 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 12:26:51 +0200
Subject: [PATCH 07/35] chore(deps): bump ibm-generative-ai
---
package.json | 4 ++--
yarn.lock | 16 ++++++++--------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/package.json b/package.json
index 2165c96..9bc5377 100644
--- a/package.json
+++ b/package.json
@@ -104,7 +104,7 @@
"zod-to-json-schema": "^3.23.2"
},
"peerDependencies": {
- "@ibm-generative-ai/node-sdk": "~3.2.1",
+ "@ibm-generative-ai/node-sdk": "~3.2.3",
"@langchain/community": "~0.2.28",
"@langchain/core": "~0.2.27",
"@langchain/langgraph": "~0.0.34",
@@ -117,7 +117,7 @@
"@commitlint/config-conventional": "^19.4.1",
"@eslint/js": "^9.9.0",
"@eslint/markdown": "^6.0.0",
- "@ibm-generative-ai/node-sdk": "~3.2.1",
+ "@ibm-generative-ai/node-sdk": "~3.2.3",
"@langchain/community": "~0.2.28",
"@langchain/core": "~0.2.27",
"@langchain/langgraph": "~0.0.34",
diff --git a/yarn.lock b/yarn.lock
index c2a874b..07c7dea 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -710,19 +710,19 @@ __metadata:
languageName: node
linkType: hard
-"@ibm-generative-ai/node-sdk@npm:~3.2.1":
- version: 3.2.1
- resolution: "@ibm-generative-ai/node-sdk@npm:3.2.1"
+"@ibm-generative-ai/node-sdk@npm:~3.2.3":
+ version: 3.2.3
+ resolution: "@ibm-generative-ai/node-sdk@npm:3.2.3"
dependencies:
"@ai-zen/node-fetch-event-source": "npm:^2.1.2"
fetch-retry: "npm:^5.0.6"
http-status-codes: "npm:^2.3.0"
openapi-fetch: "npm:^0.8.2"
- p-queue-compat: "npm:^1.0.225"
+ p-queue-compat: "npm:1.0.225"
yaml: "npm:^2.3.3"
peerDependencies:
"@langchain/core": ">=0.1.0"
- checksum: 10c0/4963f0b3ee27a881544b760358c34680db9d0edd833dcb2224967295367ec1086b553c7dabbecb674f25826ff8ed3b58430996a3d29f9565edbd50559b628888
+ checksum: 10c0/105cd6bbbdca245261532344e65b2e68f482769545ead6804e21217c00b45c1ebdac4390c93fe35364e42bf88891df3ded0b7d5587d5fbd1c0621e4416e07f38
languageName: node
linkType: hard
@@ -2499,7 +2499,7 @@ __metadata:
"@connectrpc/connect-node": "npm:^1.4.0"
"@eslint/js": "npm:^9.9.0"
"@eslint/markdown": "npm:^6.0.0"
- "@ibm-generative-ai/node-sdk": "npm:~3.2.1"
+ "@ibm-generative-ai/node-sdk": "npm:~3.2.3"
"@langchain/community": "npm:~0.2.28"
"@langchain/core": "npm:~0.2.27"
"@langchain/langgraph": "npm:~0.0.34"
@@ -2565,7 +2565,7 @@ __metadata:
zod: "npm:^3.23.8"
zod-to-json-schema: "npm:^3.23.2"
peerDependencies:
- "@ibm-generative-ai/node-sdk": ~3.2.1
+ "@ibm-generative-ai/node-sdk": ~3.2.3
"@langchain/community": ~0.2.28
"@langchain/core": ~0.2.27
"@langchain/langgraph": ~0.0.34
@@ -7237,7 +7237,7 @@ __metadata:
languageName: node
linkType: hard
-"p-queue-compat@npm:^1.0.225":
+"p-queue-compat@npm:1.0.225":
version: 1.0.225
resolution: "p-queue-compat@npm:1.0.225"
dependencies:
From 7f2f466cb6f2d35575358073be6fd222e13117bb Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 13:59:39 +0200
Subject: [PATCH 08/35] fix(example): fix code interpreter path
---
examples/agents/bee.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/examples/agents/bee.ts b/examples/agents/bee.ts
index 4e9b983..76094f9 100644
--- a/examples/agents/bee.ts
+++ b/examples/agents/bee.ts
@@ -38,8 +38,8 @@ const agent = new BeeAgent({
new PythonTool({
codeInterpreter: { url: codeInterpreterUrl },
storage: new LocalPythonStorage({
- interpreterWorkingDir: `${__dirname}/tmp/code_interpreter`,
- localWorkingDir: `${__dirname}/tmp/local`,
+ interpreterWorkingDir: `${__dirname}/../tmp/code_interpreter`,
+ localWorkingDir: `${__dirname}/../tmp/local`,
}),
}),
]
From d23d4f8f0717434bc55478253d5a8f0c71a34151 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 14:03:55 +0200
Subject: [PATCH 09/35] feat(python): add custom id support
---
src/tools/python/output.ts | 2 +-
src/tools/python/python.ts | 100 +++++++++++++++++++++++-------------
src/tools/python/storage.ts | 52 ++++++++++++++-----
3 files changed, 104 insertions(+), 50 deletions(-)
diff --git a/src/tools/python/output.ts b/src/tools/python/output.ts
index d03e5db..5a19512 100644
--- a/src/tools/python/output.ts
+++ b/src/tools/python/output.ts
@@ -37,7 +37,7 @@ export class PythonToolOutput extends ToolOutput {
getTextContent() {
const fileList = this.outputFiles
- .map((file) => `- [${file.filename}](urn:${file.hash})`)
+ .map((file) => `- [${file.filename}](urn:${file.id})`)
.join("\n");
return `The code exited with code ${this.exitCode}.
stdout:
diff --git a/src/tools/python/python.ts b/src/tools/python/python.ts
index c42d8fd..f2c8533 100644
--- a/src/tools/python/python.ts
+++ b/src/tools/python/python.ts
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-import { BaseToolOptions, BaseToolRunOptions, Tool, ToolInput } from "@/tools/base.js";
+import {
+ BaseToolOptions,
+ BaseToolRunOptions,
+ Tool,
+ ToolInput,
+ ToolInputValidationError,
+} from "@/tools/base.js";
import { createGrpcTransport } from "@connectrpc/connect-node";
import { PromiseClient, createPromiseClient } from "@connectrpc/connect";
import { CodeInterpreterService } from "bee-proto/code_interpreter/v1/code_interpreter_service_connect";
@@ -22,11 +28,12 @@ import { z } from "zod";
import { BaseLLMOutput } from "@/llms/base.js";
import { LLM } from "@/llms/index.js";
import { PromptTemplate } from "@/template.js";
-import { mapToObj } from "remeda";
-import { PythonFile, PythonStorage } from "@/tools/python/storage.js";
+import { differenceWith, isShallowEqual, isTruthy, mapToObj, unique } from "remeda";
+import { PythonStorage } from "@/tools/python/storage.js";
import { PythonToolOutput } from "@/tools/python/output.js";
import { ValidationError } from "ajv";
import { ConnectionOptions } from "node:tls";
+import { AnySchemaLike } from "@/internals/helpers/schema.js";
export interface PythonToolOptions extends BaseToolOptions {
codeInterpreter: {
@@ -58,7 +65,7 @@ export class PythonTool extends Tool {
inputFiles: z
.object(
mapToObj(files, (value) => [
- value.hash,
+ value.id,
z.literal(value.filename).describe("filename of a file"),
]),
)
@@ -69,13 +76,31 @@ export class PythonTool extends Tool {
"To access an existing file, you must specify it; otherwise, the file will not be accessible. IMPORTANT: If the file is not provided in the input, it will not be accessible.",
"The key is the final segment of a file URN, and the value is the filename. ",
files.length > 0
- ? `Example: {"${files[0].hash}":"${files[0].filename}"} -- the files will be available to the Python code in the working directory.`
+ ? `Example: {"${files[0].id}":"${files[0].filename}"} -- the files will be available to the Python code in the working directory.`
: `Example: {"e6979b7bec732b89a736fd19436ec295f6f64092c0c6c0c86a2a7f27c73519d6":"file.txt"} -- the files will be available to the Python code in the working directory.`,
].join(""),
),
});
}
+ protected validateInput(
+ schema: AnySchemaLike,
+ rawInput: unknown,
+ ): asserts rawInput is ToolInput {
+ super.validateInput(schema, rawInput);
+
+ const fileNames = Object.values(rawInput.inputFiles ?? {}).filter(Boolean) as string[];
+ const diff = differenceWith(fileNames, unique(fileNames), isShallowEqual);
+ if (diff.length > 0) {
+ throw new ToolInputValidationError(
+ [
+ `All 'inputFiles' must have a unique filenames.`,
+ `Duplicated filenames: ${diff.join(",")}`,
+ ].join("\n"),
+ );
+ }
+ }
+
protected readonly client: PromiseClient;
protected readonly preprocess;
@@ -111,22 +136,16 @@ export class PythonTool extends Tool {
}
protected async _run(input: ToolInput, options?: BaseToolRunOptions) {
- const inputFiles: PythonFile[] = Object.entries(input.inputFiles ?? {})
- .filter(([k, v]) => k && v)
- .map(([hash, filename]) => ({
- hash,
- filename: filename!,
- }));
-
- await this.storage.upload(inputFiles);
-
- // replace relative paths in "files" with absolute paths by prepending "/workspace"
- const filesInput = Object.fromEntries(
- inputFiles
- .filter((file) => file.filename)
- .map((file) => [`/workspace/${file.filename}`, file.hash]),
+ const inputFiles = await this.storage.upload(
+ Object.entries(input.inputFiles ?? {})
+ .filter(([k, v]) => Boolean(k && v))
+ .map(([id, filename]) => ({
+ id,
+ filename: filename!,
+ })),
);
+ // replace relative paths in "files" with absolute paths by prepending "/workspace"
const getSourceCode = async () => {
if (this.preprocess) {
const { llm, promptTemplate } = this.preprocess;
@@ -139,11 +158,15 @@ export class PythonTool extends Tool {
return input.code;
};
+ const prefix = "/workspace/";
+
const result = await this.client.execute(
{
sourceCode: await getSourceCode(),
executorId: this.options.executorId ?? "default",
- files: filesInput,
+ files: Object.fromEntries(
+ inputFiles.map((file) => [`${prefix}${file.filename}`, file.hash]),
+ ),
},
{ signal: options?.signal },
);
@@ -151,23 +174,26 @@ export class PythonTool extends Tool {
// replace absolute paths in "files" with relative paths by removing "/workspace/"
// skip files that are not in "/workspace"
// skip entries that are also entries in filesInput
- const prefix = "/workspace/";
- const filesOutput: PythonFile[] = Object.entries(result.files)
- .map(([k, v]): PythonFile => ({ filename: k, hash: v }))
- .filter(
- (file) =>
- file.filename.startsWith(prefix) &&
- !(
- file.filename.slice(prefix.length) in filesInput &&
- filesInput[file.filename.slice(prefix.length)] === file.hash
- ),
- )
- .map((file) => ({
- hash: file.hash,
- filename: file.filename.slice(prefix.length),
- }));
-
- await this.storage.download(filesOutput);
+ const filesOutput = await this.storage.download(
+ Object.entries(result.files)
+ .map(([k, v]) => {
+ const file = { path: k, hash: v };
+ if (!file.path.startsWith(prefix)) {
+ return;
+ }
+
+ const filename = file.path.slice(prefix.length);
+ if (inputFiles.some((input) => input.filename === filename && input.hash === file.hash)) {
+ return;
+ }
+
+ return {
+ hash: file.hash,
+ filename,
+ };
+ })
+ .filter(isTruthy),
+ );
return new PythonToolOutput(result.stdout, result.stderr, result.exitCode, filesOutput);
}
diff --git a/src/tools/python/storage.ts b/src/tools/python/storage.ts
index 031f683..1898cd5 100644
--- a/src/tools/python/storage.ts
+++ b/src/tools/python/storage.ts
@@ -25,10 +25,22 @@ import { Serializable } from "@/internals/serializable.js";
import { shallowCopy } from "@/serializer/utils.js";
export interface PythonFile {
+ id: string;
hash: string;
filename: string;
}
+export interface PythonUploadFile {
+ id: string;
+ filename: string;
+}
+
+export interface PythonDownloadFile {
+ id?: string;
+ filename: string;
+ hash: string;
+}
+
export abstract class PythonStorage extends Serializable {
/**
* List all files that code interpreter can use.
@@ -38,12 +50,12 @@ export abstract class PythonStorage extends Serializable {
/**
* Prepare subset of available files to code interpreter.
*/
- abstract upload(files: PythonFile[]): Promise;
+ abstract upload(files: PythonUploadFile[]): Promise;
/**
* Process updated/modified/deleted files from code interpreter response.
*/
- abstract download(files: PythonFile[]): Promise;
+ abstract download(files: PythonDownloadFile[]): Promise;
}
export class TemporaryStorage extends PythonStorage {
@@ -53,13 +65,20 @@ export class TemporaryStorage extends PythonStorage {
return this.files.slice();
}
- async upload() {}
+ async upload(files: PythonUploadFile[]): Promise {
+ return files.map((file) => ({
+ id: file.id,
+ hash: file.id,
+ filename: file.filename,
+ }));
+ }
- async download(files: PythonFile[]) {
+ async download(files: PythonDownloadFile[]) {
this.files = [
...this.files.filter((file) => files.every((f) => f.filename !== file.filename)),
- ...files,
+ ...files.map((file) => ({ id: file.hash, ...file })),
];
+ return this.files.slice();
}
createSnapshot() {
@@ -104,27 +123,35 @@ export class LocalPythonStorage extends PythonStorage {
return Promise.all(
files
.filter((file) => file.isFile() && !this.input.ignoredFiles.has(file.name))
- .map(async (file) => ({
- filename: file.name,
- hash: await this.computeHash(path.join(this.input.localWorkingDir.toString(), file.name)),
- })),
+ .map(async (file) => {
+ const hash = await this.computeHash(
+ path.join(this.input.localWorkingDir.toString(), file.name),
+ );
+
+ return {
+ id: hash,
+ filename: file.name,
+ hash,
+ };
+ }),
);
}
- async upload(files: PythonFile[]): Promise {
+ async upload(files: PythonUploadFile[]): Promise {
await this.init();
await Promise.all(
files.map((file) =>
copyFile(
path.join(this.input.localWorkingDir.toString(), file.filename),
- path.join(this.input.interpreterWorkingDir.toString(), file.hash),
+ path.join(this.input.interpreterWorkingDir.toString(), file.id),
),
),
);
+ return files.map((file) => ({ ...file, hash: file.id }));
}
- async download(files: PythonFile[]) {
+ async download(files: PythonDownloadFile[]) {
await this.init();
await Promise.all(
@@ -135,6 +162,7 @@ export class LocalPythonStorage extends PythonStorage {
),
),
);
+ return files.map((file) => ({ ...file, id: file.hash }));
}
protected async computeHash(file: PathLike) {
From e117284496d215e242254473b1ef8d9539d1d532 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 14:05:19 +0200
Subject: [PATCH 10/35] chore: release 0.0.5
---
CHANGELOG.md | 13 +++++++++++++
package.json | 2 +-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c046c9..93a3a86 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## [0.0.5](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.4...v0.0.5) (2024-08-30)
+
+### Features
+
+- **bam:** refactor chat preset ([5f5fa05](https://github.com/i-am-bee/bee-agent-framework/commit/5f5fa05ad502fd1e140f4e0c3048e0d629d0f16d))
+- **python:** add custom id support ([d23d4f8](https://github.com/i-am-bee/bee-agent-framework/commit/d23d4f8f0717434bc55478253d5a8f0c71a34151))
+- **watsonx:** add chat preset ([f4797b8](https://github.com/i-am-bee/bee-agent-framework/commit/f4797b80ad2712444c15f3aa050f2f2db8c1d86e))
+- **watsonx:** auto load accessToken/apiKey from env ([369da92](https://github.com/i-am-bee/bee-agent-framework/commit/369da929281932ae6151eb388601370c83c113b5))
+
+### Bug Fixes
+
+- **example:** fix code interpreter path ([7f2f466](https://github.com/i-am-bee/bee-agent-framework/commit/7f2f466cb6f2d35575358073be6fd222e13117bb))
+
## [0.0.4](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.3...v0.0.4) (2024-08-29)
### Features
diff --git a/package.json b/package.json
index 9bc5377..d57809e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bee-agent-framework",
- "version": "0.0.4",
+ "version": "0.0.5",
"license": "Apache-2.0",
"description": "Bee - LLM Agent Framework",
"author": "IBM Corp.",
From cd3320407a870f92aabe9db06efebdd46b432fcf Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 15:50:50 +0200
Subject: [PATCH 11/35] feat(react): emit `toolError` event for an invalid
input
---
src/agents/bee/runner.ts | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/agents/bee/runner.ts b/src/agents/bee/runner.ts
index be38264..049f750 100644
--- a/src/agents/bee/runner.ts
+++ b/src/agents/bee/runner.ts
@@ -260,16 +260,6 @@ export class BeeAgentRunner {
output: toolOutput.getTextContent(),
};
} catch (error) {
- if (error instanceof ToolInputValidationError) {
- this.failedAttemptsCounter.use(error);
- return {
- success: false,
- output: BeeToolInputErrorPrompt.render({
- reason: error.toString(),
- }),
- };
- }
-
await this.run.emitter.emit("toolError", {
data: {
tool,
@@ -280,6 +270,16 @@ export class BeeAgentRunner {
},
});
+ if (error instanceof ToolInputValidationError) {
+ this.failedAttemptsCounter.use(error);
+ return {
+ success: false,
+ output: BeeToolInputErrorPrompt.render({
+ reason: error.toString(),
+ }),
+ };
+ }
+
if (FrameworkError.isRetryable(error)) {
this.failedAttemptsCounter.use(error);
return {
From fe68867f58784c3ce8856b29318e700a287c28b7 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 17:16:27 +0200
Subject: [PATCH 12/35] ci: improve copyright script
Ref: #11
---
scripts/copyright.sh | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/scripts/copyright.sh b/scripts/copyright.sh
index 29b5611..4188b6d 100755
--- a/scripts/copyright.sh
+++ b/scripts/copyright.sh
@@ -12,6 +12,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+
set -e
# Path to the package.json file
@@ -27,8 +28,24 @@ fi
AUTHOR=$(jq -r '.author' "$PACKAGE_JSON_PATH")
# Check if the author property is not null or empty
-if [[ ! -n "$AUTHOR" ]]; then
+if [[ -z "$AUTHOR" ]]; then
echo "Error: author property not found in package.json"
+ exit 1
+fi
+
+# Check if 'nwa' command is not available and 'go' is available, then install 'nwa'
+if ! command -v nwa &> /dev/null && command -v go &> /dev/null; then
+ echo "Installing 'nwa' via 'go' (https://github.com/B1NARY-GR0UP/nwa)"
+ go install github.com/B1NARY-GR0UP/nwa@latest
+ # Ensure the GOPATH is added to the PATH environment variable
+ export PATH=$PATH:$(go env GOPATH)/bin
fi
-nwa add -l apache -c "$AUTHOR" src dist tests
+if command -v nwa &> /dev/null; then
+ nwa add -l apache -c "$AUTHOR" src dist tests scripts
+elif command -v docker &> /dev/null; then
+ docker run -it -v "${PWD}:/src" ghcr.io/b1nary-gr0up/nwa:main add -l apache -c "$AUTHOR" src dist tests scripts
+else
+ echo "Error: 'nwa' is not available. Either install it manually or install go/docker."
+ exit 1
+fi
From 7e05270b863dfe658e06311fa3b46874ff896f1f Mon Sep 17 00:00:00 2001
From: Ismael Faro Sertage
Date: Fri, 30 Aug 2024 14:19:34 -0500
Subject: [PATCH 13/35] docs: update README.md
adding some contexts around of the example + fix minor typos
---
README.md | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index ae87d41..543ab97 100644
--- a/README.md
+++ b/README.md
@@ -49,10 +49,11 @@ import { TokenMemory } from "bee-agent-framework/memory/tokenMemory";
import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch";
import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo";
-const llm = new OllamaChatLLM(); // default is llama3.1 (7b), it is recommended to use 70b model
+const llm = new OllamaChatLLM(); // default is llama3.1 (8B), it is recommended to use 70B model
+
const agent = new BeeAgent({
- llm, // for more explore 'bee-agent-framework/adapters'
- memory: new TokenMemory({ llm }), // for more explore 'bee-agent-framework/memory'
+ llm, // for more explore 'bee-agent-framework/adapters'
+ memory: new TokenMemory({ llm }), // for more explore 'bee-agent-framework/memory'
tools: [new DuckDuckGoSearchTool(), new OpenMeteoTool()], // for more explore 'bee-agent-framework/tools'
});
@@ -67,6 +68,8 @@ const response = await agent
console.log(`Agent 🤖 : `, response.result.text);
```
+To run this example, be sure that you have installed [ollama](https://ollama.com) with the [llama3.1](https://ollama.com/library/llama3.1) model downloaded.
+
➡️ See a more [advanced example](./examples/agents/bee.ts).
➡️ All examples can be found in the [examples](./examples) directory.
From 2689f3c9bf44cfca9f33100dcf4f92326004a7ba Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 21:32:45 +0200
Subject: [PATCH 14/35] docs: add CONTRIBUTING.md
---
CONTRIBUTING.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
create mode 100644 CONTRIBUTING.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..2e29d86
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,112 @@
+# Contributing
+
+Bee Agent Framework is an open-source project committed to bringing LLM agents to
+people of all backgrounds. This page describes how you can join the Bee
+community in this goal.
+
+## Before you start
+
+If you are new to Bee contributing, we recommend you do the following before diving into the code:
+
+- Read [Bee Overview](./docs/overview.md) to understand core concepts.
+- Read [Code of Conduct](./CODE_OF_CONDUCT.md).
+
+## Choose an issue to work on
+
+Bee uses the following labels to help non-maintainers find issues best suited to their interests and experience level:
+
+- [good first issue](https://github.com/i-am-bee/bee-agent-framework/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) - these issues are typically the simplest available to work on, ideal for newcomers. They should already be fully scoped, with a straightforward approach outlined in the descriptions.
+- [help wanted](https://github.com/i-am-bee/bee-agent-framework/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) - these issues are generally more complex than good first issues. They typically cover work that core maintainers don't currently have the capacity to implement and may require more investigation/discussion. These are great options for experienced contributors looking for something more challenging.
+
+## Set up a development environment
+
+To start contributing to the Bee Agent framework, follow these steps to set up your development environment:
+
+1. **Install Node Version Manager (NVM):** We use `.nvmrc` to specify the required Node.js version. Install [nvm](https://github.com/nvm-sh/nvm) by following the official installation instructions.
+
+2. **Install the Correct Node.js Version:** Use `nvm` to install and use the Node.js version specified in the `.nvmrc` file:
+
+```bash
+nvm install
+nvm use
+```
+
+3. **Install [Yarn](https://yarnpkg.com/) via Corepack:** This project uses Yarn as the package manager. Ensure you have Corepack enabled and install Yarn:
+
+```bash
+corepack enable
+```
+
+4. **Install Dependencies:** Install all project dependencies by running:
+
+```bash
+yarn install
+```
+
+5. **Setup environmental variables:** To run E2E Tests, you should set the following variables in your `.env` file in the repository’s root.
+
+```bash
+# At least one provider API key must be defined!
+GENAI_API_KEY=""
+OPENAI_API_KEY=""
+WATSONX_API_KEY=""
+
+WATSONX_PROJECT_ID="" # optional
+WATSONX_SPACE_ID="" # optional
+WATSONX_DEPLOYMENT_ID="" # optional
+```
+
+6. **Follow Conventional Commit Messages:** We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) to structure our commit messages. This helps maintain a clean and manageable commit history. Please use the following format:
+
+```
+():
+Type: feat, fix, chore, docs, style, refactor, perf, test, etc.
+Scope: The area of the codebase your changes affect (optional).
+Subject: A short description of the changes (required).
+```
+
+_Example:_
+
+```
+feat(watsonx): add llm streaming support
+
+Ref: #15
+```
+
+7. **Run Linters/Formatters:** Ensure your changes meet code quality standards. Run the following commands:
+
+```shell
+yarn lint # or yarn lint:fix
+yarn format # or yarn lint:format
+```
+
+8. **Run Tests:** Ensure your changes pass all tests (unit, integration, E2E). Run the following commands:
+
+```shell
+yarn test:unit
+yarn test:e2e
+```
+
+By following these steps, you'll be all set to contribute to our project! If you encounter any issues during the setup process, please feel free to open an issue.
+
+## Style and lint
+
+Bee uses the following tools to meet code quality standards and ensure a unified code style across the codebase.
+
+- [ESLint](https://eslint.org/) - Linting Utility
+- [Prettier](https://prettier.io/) - Code Formatter
+- [commitlint](https://commitlint.js.org/) - Lint commit messages according to [Conventional Commits](https://www.conventionalcommits.org/).
+
+## Issues and pull requests
+
+We use GitHub pull requests to accept contributions.
+
+While not required, opening a new issue about the bug you're fixing or the feature you're working on before you open a pull request is important in starting a discussion with the community about your work. The issue gives us a place to talk about the idea and how we can work together to implement it in the code. It also lets the community know what you're working on, and if you need help, you can reference the issue when discussing it with other community and team members.
+If you've written some code but need help finishing it, want to get initial feedback on it before finishing it, or want to share it and discuss it prior to completing the implementation, you can open a Draft pull request and prepend the title with the [WIP] tag (for Work In Progress). This will indicate to reviewers that the code in the PR isn't in its final state and will change. It also means we will only merge the commit once it is finished. You or a reviewer can remove the [WIP] tag when the code is ready to be thoroughly reviewed for merging.
+
+## Contributor Licensing Agreement
+
+Before you can submit any code, all contributors must sign a
+contributor license agreement (CLA). By signing a CLA, you're attesting
+that you are the author of the contribution, and that you're freely
+contributing it under the terms of the Apache-2.0 license.
From 0a81dffdb8f8e8e8078225f84d87aa09223f2d38 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Fri, 30 Aug 2024 21:39:15 +0200
Subject: [PATCH 15/35] docs: update contribution section
---
README.md | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 543ab97..9228dcb 100644
--- a/README.md
+++ b/README.md
@@ -49,11 +49,11 @@ import { TokenMemory } from "bee-agent-framework/memory/tokenMemory";
import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch";
import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo";
-const llm = new OllamaChatLLM(); // default is llama3.1 (8B), it is recommended to use 70B model
+const llm = new OllamaChatLLM(); // default is llama3.1 (8B), it is recommended to use 70B model
const agent = new BeeAgent({
- llm, // for more explore 'bee-agent-framework/adapters'
- memory: new TokenMemory({ llm }), // for more explore 'bee-agent-framework/memory'
+ llm, // for more explore 'bee-agent-framework/adapters'
+ memory: new TokenMemory({ llm }), // for more explore 'bee-agent-framework/memory'
tools: [new DuckDuckGoSearchTool(), new OpenMeteoTool()], // for more explore 'bee-agent-framework/tools'
});
@@ -156,11 +156,7 @@ To see more in-depth explanation see [docs](./docs/overview.md).
The Bee Agent Framework is an open-source project and we ❤️ contributions.
-### Feature contributions
-
-You can get started with any ticket market as “good first issue”.
-
-**Have an idea for a new feature?** We recommend you first talk to a maintainer prior to spending a lot of time making a pull request that may not align with the project roadmap.
+If you'd like to contribute to Bee, please take a look at our [contribution guidelines](./CONTRIBUTING.md).
### Bugs
From 889da331f809f79d322d8df5129e068e59e3a7dd Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 09:51:06 +0200
Subject: [PATCH 16/35] feat(agent): extends bee success event
---
src/agents/bee/agent.ts | 2 ++
src/agents/bee/types.ts | 6 +++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/agents/bee/agent.ts b/src/agents/bee/agent.ts
index ad7369a..e3a7b69 100644
--- a/src/agents/bee/agent.ts
+++ b/src/agents/bee/agent.ts
@@ -147,6 +147,8 @@ export class BeeAgent extends BaseAgent;
error?: Callback<{ error: Error }>;
retry?: Callback;
- success?: Callback<{ data: BaseMessage }>;
+ success?: Callback<{
+ data: BaseMessage;
+ iterations: BeeAgentRunIteration[];
+ memory: BaseMemory;
+ }>;
update?: Callback<{
data: BeeIterationResult;
update: { key: keyof BeeIterationResult; value: string };
From 716e74d4727d418b78a7af69789db93c53853c2f Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 13:23:13 +0200
Subject: [PATCH 17/35] feat(tools): update python tool output file prefix
---
src/tools/python/output.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/tools/python/output.ts b/src/tools/python/output.ts
index 5a19512..0f9c394 100644
--- a/src/tools/python/output.ts
+++ b/src/tools/python/output.ts
@@ -18,6 +18,8 @@ import { ToolOutput } from "@/tools/base.js";
import { PythonFile } from "@/tools/python/storage.js";
export class PythonToolOutput extends ToolOutput {
+ static FILE_PREFIX = "urn:bee:file";
+
constructor(
public readonly stdout: string,
public readonly stderr: string,
@@ -37,7 +39,7 @@ export class PythonToolOutput extends ToolOutput {
getTextContent() {
const fileList = this.outputFiles
- .map((file) => `- [${file.filename}](urn:${file.id})`)
+ .map((file) => `- [${file.filename}](${PythonToolOutput.FILE_PREFIX}:${file.id})`)
.join("\n");
return `The code exited with code ${this.exitCode}.
stdout:
From 6c5f045cd1315ed4ee88ecbce3e0236c42734de9 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 15:40:19 +0200
Subject: [PATCH 18/35] feat: remove modules index file
---
src/adapters/bam/llm.ts | 2 +-
src/adapters/langchain/llms/index.ts | 17 -----------------
src/adapters/langchain/llms/llm.ts | 2 +-
src/adapters/ollama/llm.ts | 2 +-
src/adapters/watsonx/llm.ts | 2 +-
src/llms/index.ts | 17 -----------------
src/memory/summarizeMemory.ts | 2 +-
src/tools/python/python.ts | 2 +-
8 files changed, 6 insertions(+), 40 deletions(-)
delete mode 100644 src/adapters/langchain/llms/index.ts
delete mode 100644 src/llms/index.ts
diff --git a/src/adapters/bam/llm.ts b/src/adapters/bam/llm.ts
index 42da622..e5a23b1 100644
--- a/src/adapters/bam/llm.ts
+++ b/src/adapters/bam/llm.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { LLM, LLMInput } from "@/llms/index.js";
+import { LLM, LLMInput } from "@/llms/llm.js";
import {
AsyncStream,
BaseLLMOutput,
diff --git a/src/adapters/langchain/llms/index.ts b/src/adapters/langchain/llms/index.ts
deleted file mode 100644
index 6831bc6..0000000
--- a/src/adapters/langchain/llms/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * Copyright 2024 IBM Corp.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-export * from "./llm.js";
diff --git a/src/adapters/langchain/llms/llm.ts b/src/adapters/langchain/llms/llm.ts
index 51cb527..8188a4e 100644
--- a/src/adapters/langchain/llms/llm.ts
+++ b/src/adapters/langchain/llms/llm.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { LLM, LLMInput } from "@/llms/index.js";
+import { LLM, LLMInput } from "@/llms/llm.js";
import { BaseLLM as LCBaseLLM } from "@langchain/core/language_models/llms";
import {
AsyncStream,
diff --git a/src/adapters/ollama/llm.ts b/src/adapters/ollama/llm.ts
index 6f5d24b..3691f35 100644
--- a/src/adapters/ollama/llm.ts
+++ b/src/adapters/ollama/llm.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { LLM, LLMInput } from "@/llms/index.js";
+import { LLM, LLMInput } from "@/llms/llm.js";
import { Emitter } from "@/emitter/emitter.js";
import {
AsyncStream,
diff --git a/src/adapters/watsonx/llm.ts b/src/adapters/watsonx/llm.ts
index e087ffc..2b54395 100644
--- a/src/adapters/watsonx/llm.ts
+++ b/src/adapters/watsonx/llm.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { LLM, LLMInput } from "@/llms/index.js";
+import { LLM, LLMInput } from "@/llms/llm.js";
import {
AsyncStream,
BaseLLMOutput,
diff --git a/src/llms/index.ts b/src/llms/index.ts
deleted file mode 100644
index 6831bc6..0000000
--- a/src/llms/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * Copyright 2024 IBM Corp.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-export * from "./llm.js";
diff --git a/src/memory/summarizeMemory.ts b/src/memory/summarizeMemory.ts
index aae39ab..861c3c9 100644
--- a/src/memory/summarizeMemory.ts
+++ b/src/memory/summarizeMemory.ts
@@ -17,7 +17,7 @@
import { BaseMessage } from "@/llms/primitives/message.js";
import { BaseMemory } from "@/memory/base.js";
import { PromptTemplate } from "@/template.js";
-import { LLM } from "@/llms/index.js";
+import { LLM } from "@/llms/llm.js";
import { BaseLLMOutput } from "@/llms/base.js";
import { shallowCopy } from "@/serializer/utils.js";
diff --git a/src/tools/python/python.ts b/src/tools/python/python.ts
index f2c8533..8995f40 100644
--- a/src/tools/python/python.ts
+++ b/src/tools/python/python.ts
@@ -26,7 +26,7 @@ import { PromiseClient, createPromiseClient } from "@connectrpc/connect";
import { CodeInterpreterService } from "bee-proto/code_interpreter/v1/code_interpreter_service_connect";
import { z } from "zod";
import { BaseLLMOutput } from "@/llms/base.js";
-import { LLM } from "@/llms/index.js";
+import { LLM } from "@/llms/llm.js";
import { PromptTemplate } from "@/template.js";
import { differenceWith, isShallowEqual, isTruthy, mapToObj, unique } from "remeda";
import { PythonStorage } from "@/tools/python/storage.js";
From 9d6f6bcd8289c694388da3c4ab5c5b77ffcece8b Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 15:41:38 +0200
Subject: [PATCH 19/35] chore: release 0.0.6
---
CHANGELOG.md | 9 +++++++++
package.json | 2 +-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93a3a86..ce7f345 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## [0.0.6](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.5...v0.0.6) (2024-09-02)
+
+### Features
+
+- **agent:** extends bee success event ([889da33](https://github.com/i-am-bee/bee-agent-framework/commit/889da331f809f79d322d8df5129e068e59e3a7dd))
+- **react:** emit `toolError` event for an invalid input ([cd33204](https://github.com/i-am-bee/bee-agent-framework/commit/cd3320407a870f92aabe9db06efebdd46b432fcf))
+- remove modules index file ([6c5f045](https://github.com/i-am-bee/bee-agent-framework/commit/6c5f045cd1315ed4ee88ecbce3e0236c42734de9))
+- **tools:** update python tool output file prefix ([716e74d](https://github.com/i-am-bee/bee-agent-framework/commit/716e74d4727d418b78a7af69789db93c53853c2f))
+
## [0.0.5](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.4...v0.0.5) (2024-08-30)
### Features
diff --git a/package.json b/package.json
index d57809e..49e98fc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bee-agent-framework",
- "version": "0.0.5",
+ "version": "0.0.6",
"license": "Apache-2.0",
"description": "Bee - LLM Agent Framework",
"author": "IBM Corp.",
From 6d67b6f81eb0d3011def997fe201c30aa623fe6e Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 18:36:42 +0200
Subject: [PATCH 20/35] ci: update actions
---
.github/workflows/e2e-tests.yml | 43 +++++++++++++++++++++++++++++++++
.github/workflows/main.yml | 38 ++---------------------------
2 files changed, 45 insertions(+), 36 deletions(-)
create mode 100644 .github/workflows/e2e-tests.yml
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
new file mode 100644
index 0000000..d886f68
--- /dev/null
+++ b/.github/workflows/e2e-tests.yml
@@ -0,0 +1,43 @@
+name: CI
+
+on:
+ push:
+ branches: ["main"]
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ name: Tests
+ timeout-minutes: 20
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: [18.x, 20.x, 22.x]
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Enable Corepack
+ run: corepack enable
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: "yarn"
+ - name: Install dependencies
+ run: yarn install --immutable
+ - name: E2E Tests
+ env:
+ GENAI_API_KEY: ${{ secrets.GENAI_API_KEY }}
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ # TODO: enable WatsonX later
+ # WATSONX_API_KEY: ${{ secrets.WATSONX_API_KEY }}
+ # WATSONX_PROJECT_ID: ${{ secrets.WATSONX_PROJECT_ID }}
+ # WATSONX_SPACE_ID: ${{ secrets.WATSONX_SPACE_ID }}
+ # WATSONX_DEPLOYMENT_ID: ${{ secrets.WATSONX_DEPLOYMENT_ID }}
+ run: yarn test:e2e
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 85779a7..febdb2e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -11,9 +11,9 @@ concurrency:
cancel-in-progress: true
jobs:
- lint:
+ main:
timeout-minutes: 20
- name: Lint & Build
+ name: Lint & Build & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -34,39 +34,5 @@ jobs:
run: yarn commitlint --verbose --from "${{ github.event.pull_request.base.sha || github.event.commits[0].id }}" --to "${{ github.event.pull_request.head.sha || github.event.head_commit.id }}"
- name: Build
run: yarn build
-
- test:
- name: Tests
- timeout-minutes: 20
- needs:
- - lint
- runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- node-version: [18.x, 20.x, 22.x]
-
- steps:
- - uses: actions/checkout@v4
- - name: Enable Corepack
- run: corepack enable
- - name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v4
- with:
- node-version: ${{ matrix.node-version }}
- cache: "yarn"
- - name: Install dependencies
- run: yarn install --immutable
- name: Unit Tests
run: yarn test:unit
- - name: E2E Tests
- env:
- GENAI_API_KEY: ${{ secrets.GENAI_API_KEY }}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- # TODO: enable WatsonX later
- # WATSONX_API_KEY: ${{ secrets.WATSONX_API_KEY }}
- # WATSONX_PROJECT_ID: ${{ secrets.WATSONX_PROJECT_ID }}
- # WATSONX_SPACE_ID: ${{ secrets.WATSONX_SPACE_ID }}
- # WATSONX_DEPLOYMENT_ID: ${{ secrets.WATSONX_DEPLOYMENT_ID }}
- run: yarn test:e2e
From 18b96fc04ef362a4d600daa96b38bb34ab6ef2c7 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 18:37:37 +0200
Subject: [PATCH 21/35] chore: remove comments
---
src/emitter/types.ts | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/src/emitter/types.ts b/src/emitter/types.ts
index 473ddd9..3e82395 100644
--- a/src/emitter/types.ts
+++ b/src/emitter/types.ts
@@ -44,13 +44,3 @@ export interface EventMeta {
context: C;
trace?: EventTrace;
}
-
-// CREATED
-// Agent onStart({..., duration: 0, id: 1, parentId: null })
-// LLM onStart({..., duration: 0, id: 2, parentId: 1, groupId: xxx })
-// LLM onNewToken({..., duration: 10, id: 3, parentId: 1, groupId: xxx })
-// LLM onNewToken({..., duration:5, id: 4, parentId: 1, groupId: xxx})
-// LLM onNewToken({..., duration: 7, id: 5, parentId: 1, groupId: xxx})
-// LLM onNewToken({..., duration:20, id: 6, parentId: 1, groupId: xxx})
-// LLM onEnd({..., duration:10+5+7+20, groupId: xxx})
-// DESTROYED
From 16ad3c91a37bb2978741026ee501b61c233e69d3 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 18:42:07 +0200
Subject: [PATCH 22/35] ci: update action
---
.github/workflows/e2e-tests.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index d886f68..aa5e577 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -3,6 +3,8 @@ name: CI
on:
push:
branches: ["main"]
+ paths-ignore:
+ - "**/*.md"
workflow_dispatch:
concurrency:
From ec9fb96a26da851520104271124d246003afd131 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 19:38:53 +0200
Subject: [PATCH 23/35] feat(agent): group iterations, add meta property
---
src/agents/bee/agent.ts | 21 +++++++++----
src/agents/bee/runner.ts | 60 ++++++++++++++++++++----------------
src/agents/bee/types.ts | 16 +++++++---
tests/e2e/agents/bee.test.ts | 6 ++++
4 files changed, 67 insertions(+), 36 deletions(-)
diff --git a/src/agents/bee/agent.ts b/src/agents/bee/agent.ts
index e3a7b69..50a69a3 100644
--- a/src/agents/bee/agent.ts
+++ b/src/agents/bee/agent.ts
@@ -26,6 +26,7 @@ import { Emitter } from "@/emitter/emitter.js";
import {
BeeAgentRunIteration,
BeeCallbacks,
+ BeeMeta,
BeeRunInput,
BeeRunOptions,
BeeRunOutput,
@@ -94,11 +95,12 @@ export class BeeAgent extends BaseAgent= maxIterations) {
+ const meta: BeeMeta = { iteration: iterations.length + 1 };
+ if (meta.iteration > maxIterations) {
throw new BeeAgentError(
`Agent was not able to resolve the task in ${maxIterations} iterations.`,
[],
@@ -106,18 +108,24 @@ export class BeeAgent extends BaseAgent>,
protected readonly input: BeeInput,
protected readonly options: BeeRunOptions,
public readonly memory: TokenMemory,
@@ -54,12 +52,7 @@ export class BeeAgentRunner {
this.failedAttemptsCounter = new RetryCounter(options?.execution?.totalMaxRetries, AgentError);
}
- static async create(
- run: RunContext>,
- input: BeeInput,
- options: BeeRunOptions,
- prompt: string,
- ) {
+ static async create(input: BeeInput, options: BeeRunOptions, prompt: string) {
const memory = new TokenMemory({
llm: input.llm,
capacityThreshold: 0.85,
@@ -118,25 +111,29 @@ export class BeeAgentRunner {
}),
]);
- return new BeeAgentRunner(run, input, options, memory);
+ return new BeeAgentRunner(input, options, memory);
}
- async llm(): Promise {
- const emitter = this.run.emitter;
+ async llm(input: {
+ emitter: Emitter;
+ signal: AbortSignal;
+ meta: BeeMeta;
+ }): Promise {
+ const { emitter, signal, meta } = input;
return new Retryable({
- onRetry: () => emitter.emit("retry", undefined),
+ onRetry: () => emitter.emit("retry", { meta }),
onError: async (error) => {
- await emitter.emit("error", { error });
+ await emitter.emit("error", { error, meta });
this.failedAttemptsCounter.use(error);
},
executor: async () => {
- await emitter.emit("start", undefined);
+ await emitter.emit("start", { meta });
const outputParser = new BeeOutputParser();
const llmOutput = await this.input.llm
.generate(this.memory.messages.slice(), {
- signal: this.run.signal,
+ signal,
stream: true,
guided: {
regex:
@@ -149,7 +146,7 @@ export class BeeAgentRunner {
await emitter.emit(type === "full" ? "update" : "partialUpdate", {
data: state,
update,
- meta: { success: true },
+ meta: { success: true, ...meta },
});
});
@@ -177,12 +174,19 @@ export class BeeAgentRunner {
},
config: {
maxRetries: this.options.execution?.maxRetriesPerStep,
- signal: this.run.signal,
+ signal,
},
}).get();
}
- async tool(iteration: BeeIterationToolResult): Promise<{ output: string; success: boolean }> {
+ async tool(input: {
+ iteration: BeeIterationToolResult;
+ signal: AbortSignal;
+ emitter: Emitter;
+ meta: BeeMeta;
+ }): Promise<{ output: string; success: boolean }> {
+ const { iteration, signal, emitter, meta } = input;
+
const tool = this.input.tools.find(
(tool) => tool.name.trim().toUpperCase() == iteration.tool_name?.toUpperCase(),
);
@@ -202,7 +206,7 @@ export class BeeAgentRunner {
}
const options = await (async () => {
const baseOptions: BaseToolRunOptions = {
- signal: this.run.signal,
+ signal,
};
const customOptions = await this.options.modifiers?.getToolRunOptions?.({
tool,
@@ -214,11 +218,11 @@ export class BeeAgentRunner {
return new Retryable({
config: {
- signal: this.run.signal,
+ signal,
maxRetries: this.options.execution?.maxRetriesPerStep,
},
onError: async (error) => {
- await this.run.emitter.emit("toolError", {
+ await emitter.emit("toolError", {
data: {
iteration,
tool,
@@ -226,22 +230,24 @@ export class BeeAgentRunner {
options,
error: FrameworkError.ensure(error),
},
+ meta,
});
this.failedAttemptsCounter.use(error);
},
executor: async () => {
- await this.run.emitter.emit("toolStart", {
+ await emitter.emit("toolStart", {
data: {
tool,
input: iteration.tool_input,
options,
iteration,
},
+ meta,
});
try {
const toolOutput: ToolOutput = await tool.run(iteration.tool_input, options);
- await this.run.emitter.emit("toolSuccess", {
+ await emitter.emit("toolSuccess", {
data: {
tool,
input: iteration.tool_input,
@@ -249,6 +255,7 @@ export class BeeAgentRunner {
result: toolOutput,
iteration,
},
+ meta,
});
if (toolOutput.isEmpty()) {
@@ -260,7 +267,7 @@ export class BeeAgentRunner {
output: toolOutput.getTextContent(),
};
} catch (error) {
- await this.run.emitter.emit("toolError", {
+ await emitter.emit("toolError", {
data: {
tool,
input: iteration.tool_input,
@@ -268,6 +275,7 @@ export class BeeAgentRunner {
error,
iteration,
},
+ meta,
});
if (error instanceof ToolInputValidationError) {
diff --git a/src/agents/bee/types.ts b/src/agents/bee/types.ts
index b60642a..1a5f66c 100644
--- a/src/agents/bee/types.ts
+++ b/src/agents/bee/types.ts
@@ -54,18 +54,23 @@ export interface BeeRunOptions {
};
}
-export interface BeeUpdateMeta {
+export interface BeeMeta {
+ iteration: number;
+}
+
+export interface BeeUpdateMeta extends BeeMeta {
success: boolean;
}
export interface BeeCallbacks {
- start?: Callback;
- error?: Callback<{ error: Error }>;
- retry?: Callback;
+ start?: Callback<{ meta: BeeMeta }>;
+ error?: Callback<{ error: Error; meta: BeeMeta }>;
+ retry?: Callback<{ meta: BeeMeta }>;
success?: Callback<{
data: BaseMessage;
iterations: BeeAgentRunIteration[];
memory: BaseMemory;
+ meta: BeeMeta;
}>;
update?: Callback<{
data: BeeIterationResult;
@@ -84,6 +89,7 @@ export interface BeeCallbacks {
options: BaseToolRunOptions;
iteration: BeeIterationToolResult;
};
+ meta: BeeMeta;
}>;
toolSuccess?: Callback<{
data: {
@@ -93,6 +99,7 @@ export interface BeeCallbacks {
result: ToolOutput;
iteration: BeeIterationToolResult;
};
+ meta: BeeMeta;
}>;
toolError?: Callback<{
data: {
@@ -102,5 +109,6 @@ export interface BeeCallbacks {
error: ToolError;
iteration: BeeIterationToolResult;
};
+ meta: BeeMeta;
}>;
}
diff --git a/tests/e2e/agents/bee.test.ts b/tests/e2e/agents/bee.test.ts
index a582546..6ccdb0d 100644
--- a/tests/e2e/agents/bee.test.ts
+++ b/tests/e2e/agents/bee.test.ts
@@ -25,6 +25,7 @@ import { createCallbackRegister } from "@tests/e2e/utils.js";
import { omitEmptyValues } from "@/internals/helpers/object.js";
import * as process from "node:process";
import { createChatLLM } from "@tests/utils/llmFactory.js";
+import { BeeMeta } from "@/agents/bee/types.js";
describe("Bee Agent", () => {
const createAgent = () => {
@@ -97,6 +98,11 @@ describe("Bee Agent", () => {
},
)
.observe((emitter) => {
+ let lastIteration = 0;
+ emitter.match("*", (data: { meta: BeeMeta }) => {
+ expect(data.meta.iteration >= lastIteration);
+ lastIteration = data.meta.iteration;
+ });
emitter.registerCallbacks({
success: callbacks.create("success", {
check: ({ data }) => {
From 42a7c10a35fbb39c47b19d520dc2e2a37ea68be5 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Mon, 2 Sep 2024 19:41:32 +0200
Subject: [PATCH 24/35] chore: release 0.0.7
---
CHANGELOG.md | 6 ++++++
package.json | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce7f345..7c4e41b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## [0.0.7](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.6...v0.0.7) (2024-09-02)
+
+### Features
+
+- **agent:** group iterations, add meta property ([ec9fb96](https://github.com/i-am-bee/bee-agent-framework/commit/ec9fb96a26da851520104271124d246003afd131))
+
## [0.0.6](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.5...v0.0.6) (2024-09-02)
### Features
diff --git a/package.json b/package.json
index 49e98fc..c7f3b14 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bee-agent-framework",
- "version": "0.0.6",
+ "version": "0.0.7",
"license": "Apache-2.0",
"description": "Bee - LLM Agent Framework",
"author": "IBM Corp.",
From 3fd62fee223d313ef861f6a81b5e2a5a662e2564 Mon Sep 17 00:00:00 2001
From: Graham White
Date: Mon, 2 Sep 2024 21:01:55 +0100
Subject: [PATCH 25/35] docs: initial commit of developer information about
tools (#7)
---
README.md | 4 +-
docs/overview.md | 2 +-
docs/tools.md | 122 ++++++++++++++++++++++++++++++
examples/tools/helloWorld.ts | 32 ++++++++
examples/tools/openLibrary.ts | 136 ++++++++++++++++++++++++++++++++++
5 files changed, 293 insertions(+), 3 deletions(-)
create mode 100644 docs/tools.md
create mode 100644 examples/tools/helloWorld.ts
create mode 100644 examples/tools/openLibrary.ts
diff --git a/README.md b/README.md
index 9228dcb..0afb163 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
- Open-source framework for building, deploying, and serving powerful agentic workflows at scale.
+ Open-source framework for building, deploying, and serving powerful agentic workflows at scale.
The Bee framework makes it easy to build agentic worfklows with leading open-source and proprietary models. We’re working on bringing model-agnostic support to any LLM to help developers avoid model provider lock-in.
@@ -15,7 +15,7 @@ The Bee framework makes it easy to build agentic worfklows with leading open-sou
## Key Features
- 🤖 **AI agents**: Use our powerful [Bee agent](https://github.com/i-am-bee/bee-agent-framework/tree/main?tab=readme-ov-file#get-started-with-bee) or [build your own](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/overview.md#agents).
-- 🛠️ **Tools**: Use our [built-in tools](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/overview.md#tools) or create your own in Javascript/Python.
+- 🛠️ **Tools**: Use our [built-in tools](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/overview.md#tools) or [create your own](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/tools.md) in Javascript/Python.
- 👩💻 **Code interpreter**: Run code safely in a [sandbox container](https://github.com/i-am-bee/bee-code-interpreter).
- 💾 **Memory**: Multiple [strategies](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/overview.md#memory) to optimize token spend.
- ⏸️ **Serialization** Handle complex agentic workflows and easily pause/resume them [without losing state](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/overview.md#serializer).
diff --git a/docs/overview.md b/docs/overview.md
index 5768312..0ef78ec 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -105,7 +105,7 @@ The framework provides out-of-the-box tools.
| `OpenMeteoTool` | Retrieves current, previous, or upcoming weather for a given destination. |
| ➕ [Request](https://github.com/i-am-bee/bee-agent-framework/discussions) | |
-To create your own tool, you need to either implement the `BaseTool` class ([example](../examples/tools/customHttpRequest.ts)) or use `DynamicTool.`
+To create your own tool, you need to either implement the `Tool` class or use `DynamicTool.` More information is available in the [tool documentation](tools.md).
### Cache
diff --git a/docs/tools.md b/docs/tools.md
new file mode 100644
index 0000000..8e29fbe
--- /dev/null
+++ b/docs/tools.md
@@ -0,0 +1,122 @@
+# Tools
+
+## Introduction
+
+Tools in the context of an agent refer to additional functionalities or capabilities integrated with the agent to perform specific tasks beyond text processing.
+
+These tools extend the agent's abilities, allowing it to interact with external systems, access information, and execute actions.
+
+## Development
+
+### Writing a new tool
+
+To create a tool, the `Tool` class must be extended/ implemented or `DynamicTool` created from [tools base](../src/tools/base.ts). When starting to write tools, it is recommended to implement the `Tool` class. The `DynamicTool` class is an extension of the `Tool` class that allows for dynamic forms of input. Refer to the list of [examples](#examples) for some simple examples or any of the built-in tools in order to guide the creation of new tools.
+
+Tools MUST do the following:
+
+- Implement the `Tool` class:
+
+ `MyNewToolOutput` is required, must be an implementation of `ToolOutput` such as `StringToolOutput` or `JSONToolOutput`
+
+ `ToolOptions` is optional (default BaseToolOptions), constructor parameters that are passed during tool creation
+
+ `ToolRunOptions` is optional (default BaseToolRunOptions), optional parameters that are passed to the run method
+
+ ```typescript
+ import { BaseToolOptions, BaseToolRunOptions } from "bee-agent-framework/tools/base";
+
+ type ToolOptions = BaseToolOptions;
+ type ToolRunOptions = BaseToolRunOptions;
+
+ export class MyNewTool extends Tool {
+ // tool implementation
+ }
+ ```
+
+- Be given a unique name:
+
+ Note: Convention and best practice is to set the tool's name to the name of its class
+
+ ```typescript
+ name = "MyNewTool";
+ ```
+
+- Provide a natural language description of what the tool does:
+
+ ❗Important: this description is used by the agent to determine when the tool should be used. It's probably the most important aspect of your tool and you should experiment with different natural language descriptions to ensure the tool is used in the correct circumstances.
+
+ ```typescript
+ description = "Takes X action when given Y input resulting in Z output";
+ ```
+
+- Declare an input schema:
+
+ This is used to define the format of the input to your tool. The agent will formalise the natural language input(s) it has received and structure them into the fields described in the tool's input. The input schema can be specified using [Zod](https://github.com/colinhacks/zod) (recommended) or JSONSchema. It must be a function (either sync or async). Zod effects (e.g. `z.object().transform(...)`) are not supported. The return value of `inputSchema` must always be an object and pass validation by the `validateSchema()` function defined in [schema.ts](../src/internals/helpers/schema.ts).
+
+
+
+ ```typescript
+ inputSchema() {
+ // any Zod definition is good here, this is typical simple example
+ return z.object({
+ // list of key-value pairs
+ });
+ }
+ ```
+
+- Implement initialisation:
+
+ The unnamed static block is executed when your tool is called for the first time. It is used for registering your tool as `serializable` to the agent and any other custom initialisation that may be required.
+
+
+
+ ```typescript
+ static {
+ this.register();
+ }
+ ```
+
+- Implement the `_run()` method:
+
+
+
+ ```typescript
+ protected async _run(input: ToolInput, options?: BaseToolRunOptions) {
+ // insert custom code here
+ // MUST: return an instance of the output type specified in the tool class definition
+ // MAY: throw an instance of ToolError upon unrecoverable error conditions encountered by the tool
+ }
+ ```
+
+### Using tools with agents
+
+In order for a tool to be of some utility within an agent, you must enable the agent with knowledge of the tool. To do this, the tool code module must be imported into the agent and passed to the tools array during creation of a `BeeAgent`. An example can be found in the [bee agent](../examples/agents/bee.ts) or you can use a code snippet such as the one below that creates an agent with the built-in [ArXiv tool](../src/tools/arxiv.ts):
+
+```typescript
+import { ArXivTool } from "bee-agent-framework/tools/arxiv";
+
+const llm = new OllamaChatLLM({
+ modelId: "insert-model-id-here",
+});
+
+const agent = new BeeAgent({
+ llm,
+ memory: new TokenMemory({ llm }),
+ tools: [new ArXivTool()],
+});
+```
+
+## Examples
+
+### Hello World
+
+The simplest form of an example tool is the [helloworld](../examples/tools/helloWorld.ts) tool. This example implements the Tool class and will simply prepend the word "Hello" to a given input such as "Please give a special greeting to Bee!". It is not intended for usage with any agents since the functionality provided is highly trivial. However, it may serve as a starter template for writing new tools.
+
+### Open Library
+
+The [openlibrary](../examples/tools/openLibrary.ts) tool allows an agent to query the [Open Library](https://openlibrary.org/) via its [book search API](https://openlibrary.org/dev/docs/api/search). This functionality injects knowledge about book metadata (not book content) into an agent. It serves as an example for several key aspects of tool creation:
+
+- Implementing the `Tool` class
+- Specifying the input schema to a tool
+- Calling out to a third-party service and handling responses and errors
+- Using `JSONToolOutput` to return JSON formatted data to the agent
diff --git a/examples/tools/helloWorld.ts b/examples/tools/helloWorld.ts
new file mode 100644
index 0000000..a8a3b20
--- /dev/null
+++ b/examples/tools/helloWorld.ts
@@ -0,0 +1,32 @@
+import {
+ BaseToolOptions,
+ BaseToolRunOptions,
+ StringToolOutput,
+ Tool,
+ ToolInput,
+} from "@/tools/base.js";
+import { z } from "zod";
+
+type ToolOptions = BaseToolOptions;
+type ToolRunOptions = BaseToolRunOptions;
+
+export class HelloWorldTool extends Tool {
+ name = "HelloWorld";
+ description = "Says hello when asked for a special greeting.";
+
+ inputSchema() {
+ return z.object({
+ identifier: z
+ .string()
+ .describe("The identifier (person, object, animal, etc.) used to when saying Hello"),
+ });
+ }
+
+ static {
+ this.register();
+ }
+
+ protected async _run(input: ToolInput): Promise {
+ return new StringToolOutput(`Hello, ${input.identifier}`);
+ }
+}
diff --git a/examples/tools/openLibrary.ts b/examples/tools/openLibrary.ts
new file mode 100644
index 0000000..4b28fc3
--- /dev/null
+++ b/examples/tools/openLibrary.ts
@@ -0,0 +1,136 @@
+import {
+ BaseToolOptions,
+ BaseToolRunOptions,
+ Tool,
+ ToolInput,
+ JSONToolOutput,
+ ToolError,
+} from "@/tools/base.js";
+import { z } from "zod";
+import { createURLParams } from "@/internals/fetcher.js";
+
+type ToolOptions = BaseToolOptions;
+type ToolRunOptions = BaseToolRunOptions;
+
+export interface OpenLibraryResponse {
+ numFound: number;
+ start: number;
+ numFoundExact: boolean;
+ num_found: number;
+ q: string;
+ offset: number;
+ docs: {
+ _version_: number;
+ key: string;
+ title: string;
+ subtitle: string;
+ alternative_title: string;
+ alternative_subtitle: string;
+ cover_i: number;
+ ebook_access: string;
+ ebook_count_i: number;
+ edition_count: number;
+ edition_key: string[];
+ format: string[];
+ publish_date: string[];
+ lccn: string[];
+ ia: string[];
+ oclc: string[];
+ public_scan_b: boolean;
+ isbn: string[];
+ contributor: string[];
+ publish_place: string[];
+ publisher: string[];
+ seed: string[];
+ first_sentence: string[];
+ author_key: string[];
+ author_name: string[];
+ author_alternative_name: string[];
+ subject: string[];
+ person: string[];
+ place: string[];
+ time: string[];
+ has_fulltext: boolean;
+ title_suggest: string;
+ title_sort: string;
+ type: string;
+ publish_year: number[];
+ language: string[];
+ last_modified_i: number;
+ number_of_pages_median: number;
+ place_facet: string[];
+ publisher_facet: string[];
+ author_facet: string[];
+ first_publish_year: number;
+ ratings_count_1: number;
+ ratings_count_2: number;
+ ratings_count_3: number;
+ ratings_count_4: number;
+ ratings_count_5: number;
+ ratings_average: number;
+ ratings_sortable: number;
+ ratings_count: number;
+ readinglog_count: number;
+ want_to_read_count: number;
+ currently_reading_count: number;
+ already_read_count: number;
+ subject_key: string[];
+ person_key: string[];
+ place_key: string[];
+ subject_facet: string[];
+ time_key: string[];
+ lcc: string[];
+ ddc: string[];
+ lcc_sort: string;
+ ddc_sort: string;
+ }[];
+}
+
+export class OpenLibraryToolOutput extends JSONToolOutput {
+ isEmpty(): boolean {
+ return !this.result || this.result.numFound === 0 || this.result.docs.length === 0;
+ }
+}
+
+export class OpenLibraryTool extends Tool {
+ name = "OpenLibrary";
+ description =
+ "Provides access to a library of books with information about book titles, authors, contributors, publication dates, publisher and isbn.";
+
+ inputSchema() {
+ return z
+ .object({
+ title: z.string(),
+ author: z.string(),
+ isbn: z.string(),
+ subject: z.string(),
+ place: z.string(),
+ person: z.string(),
+ publisher: z.string(),
+ })
+ .partial();
+ }
+
+ static {
+ this.register();
+ }
+
+ protected async _run(input: ToolInput, options?: ToolRunOptions) {
+ const params = createURLParams(input);
+ const url = `https://openlibrary.org/search.json?${decodeURIComponent(params.toString())}`;
+ const response = await fetch(url, {
+ signal: options?.signal,
+ });
+ if (!response.ok) {
+ throw new ToolError("Request to Open Library API has failed!", [
+ new Error(await response.text()),
+ ]);
+ }
+ try {
+ const json = await response.json();
+ return new OpenLibraryToolOutput(json);
+ } catch (e) {
+ throw new ToolError("Request to Open Library has failed to parse!", [e]);
+ }
+ }
+}
From 0e254504cb197832fa5ed98188d0f62489def7c2 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 13:33:43 +0200
Subject: [PATCH 26/35] feat(tool): stop using getters in custom tool
---
src/tools/custom.ts | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/tools/custom.ts b/src/tools/custom.ts
index ab75a24..9a56093 100644
--- a/src/tools/custom.ts
+++ b/src/tools/custom.ts
@@ -46,12 +46,9 @@ function createCodeInterpreterClient(url: string) {
}
export class CustomTool extends Tool {
- public get name() {
- return this.options.name;
- }
- public get description() {
- return this.options.description;
- }
+ name: string;
+ description: string;
+
public inputSchema() {
return this.options.inputSchema;
}
@@ -69,6 +66,8 @@ export class CustomTool extends Tool {
validate(options, toolOptionsSchema);
super(options);
this.client = client || createCodeInterpreterClient(options.codeInterpreterUrl);
+ this.name = options.name;
+ this.description = options.description;
}
protected async _run(input: any, options: BaseToolRunOptions) {
From 58c89d93b737e5221ffb2f4c63f8e1cf508a4c33 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 14:03:08 +0200
Subject: [PATCH 27/35] feat(agent): add support for overriding templates
---
src/agents/bee/agent.ts | 5 +++--
src/agents/bee/prompts.ts | 3 +--
src/agents/bee/runner.ts | 18 +++++++++++-------
src/agents/bee/types.ts | 15 +++++++++++++++
4 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/src/agents/bee/agent.ts b/src/agents/bee/agent.ts
index 50a69a3..6753574 100644
--- a/src/agents/bee/agent.ts
+++ b/src/agents/bee/agent.ts
@@ -20,11 +20,12 @@ import { BaseMemory } from "@/memory/base.js";
import { ChatLLM, ChatLLMOutput } from "@/llms/chat.js";
import { BaseMessage, Role } from "@/llms/primitives/message.js";
import { AgentMeta } from "@/agents/types.js";
-import { BeeAgentTemplate, BeeAssistantPrompt } from "@/agents/bee/prompts.js";
+import { BeeAssistantPrompt } from "@/agents/bee/prompts.js";
import * as R from "remeda";
import { Emitter } from "@/emitter/emitter.js";
import {
BeeAgentRunIteration,
+ BeeAgentTemplates,
BeeCallbacks,
BeeMeta,
BeeRunInput,
@@ -40,8 +41,8 @@ export interface BeeInput {
llm: ChatLLM;
tools: AnyTool[];
memory: BaseMemory;
- promptTemplate?: BeeAgentTemplate;
meta?: AgentMeta;
+ templates?: BeeAgentTemplates;
}
export class BeeAgent extends BaseAgent {
diff --git a/src/agents/bee/prompts.ts b/src/agents/bee/prompts.ts
index 89d44b4..f7cfb39 100644
--- a/src/agents/bee/prompts.ts
+++ b/src/agents/bee/prompts.ts
@@ -16,7 +16,7 @@
import { PromptTemplate } from "@/template.js";
-export const BeeAgentSystemPrompt = new PromptTemplate({
+export const BeeSystemPrompt = new PromptTemplate({
variables: ["instructions", "tools", "tool_names"] as const,
defaults: {
instructions: "You are a helpful assistant that uses tools to answer questions.",
@@ -84,7 +84,6 @@ Responses must always have the following structure:
- IMPORTANT: Lines 'Thought', 'Tool Name', 'Tool Caption', 'Tool Input', 'Tool Output' and 'Final Answer' must be sent within a single message.
`,
});
-export type BeeAgentTemplate = typeof BeeAgentSystemPrompt;
export const BeeAssistantPrompt = new PromptTemplate({
variables: ["thought", "toolName", "toolCaption", "toolInput", "toolOutput", "finalAnswer"],
diff --git a/src/agents/bee/runner.ts b/src/agents/bee/runner.ts
index 6988ab0..2925a4d 100644
--- a/src/agents/bee/runner.ts
+++ b/src/agents/bee/runner.ts
@@ -27,7 +27,7 @@ import { FrameworkError } from "@/errors.js";
import { BeeInput } from "@/agents/bee/agent.js";
import { RetryCounter } from "@/internals/helpers/counter.js";
import {
- BeeAgentSystemPrompt,
+ BeeSystemPrompt,
BeeToolErrorPrompt,
BeeToolInputErrorPrompt,
BeeToolNoResultsPrompt,
@@ -86,11 +86,10 @@ export class BeeAgentRunner {
},
},
});
- const template = input.promptTemplate ?? BeeAgentSystemPrompt;
await memory.addMany([
BaseMessage.of({
role: Role.SYSTEM,
- text: template.render({
+ text: (input.templates?.system ?? BeeSystemPrompt).render({
tools: await Promise.all(
input.tools.map(async (tool) => ({
name: tool.name,
@@ -105,7 +104,7 @@ export class BeeAgentRunner {
...input.memory.messages,
BaseMessage.of({
role: Role.USER,
- text: BeeUserPrompt.clone().render({
+ text: (input.templates?.user ?? BeeUserPrompt).render({
input: prompt.trim() ? prompt : "Empty message.",
}),
}),
@@ -259,7 +258,8 @@ export class BeeAgentRunner {
});
if (toolOutput.isEmpty()) {
- return { output: BeeToolNoResultsPrompt.render({}), success: true };
+ const template = this.input.templates?.toolNoResultError ?? BeeToolNoResultsPrompt;
+ return { output: template.render({}), success: true };
}
return {
@@ -280,9 +280,11 @@ export class BeeAgentRunner {
if (error instanceof ToolInputValidationError) {
this.failedAttemptsCounter.use(error);
+
+ const template = this.input.templates?.toolInputError ?? BeeToolInputErrorPrompt;
return {
success: false,
- output: BeeToolInputErrorPrompt.render({
+ output: template.render({
reason: error.toString(),
}),
};
@@ -290,9 +292,11 @@ export class BeeAgentRunner {
if (FrameworkError.isRetryable(error)) {
this.failedAttemptsCounter.use(error);
+
+ const template = this.input.templates?.toolError ?? BeeToolErrorPrompt;
return {
success: false,
- output: BeeToolErrorPrompt.render({
+ output: template.render({
reason: FrameworkError.ensure(error).explain(),
}),
};
diff --git a/src/agents/bee/types.ts b/src/agents/bee/types.ts
index 1a5f66c..d2009b5 100644
--- a/src/agents/bee/types.ts
+++ b/src/agents/bee/types.ts
@@ -20,6 +20,13 @@ import { BaseMemory } from "@/memory/base.js";
import { BaseMessage } from "@/llms/primitives/message.js";
import { Callback } from "@/emitter/types.js";
import { AnyTool, BaseToolRunOptions, Tool, ToolError, ToolOutput } from "@/tools/base.js";
+import {
+ BeeSystemPrompt,
+ BeeToolErrorPrompt,
+ BeeToolInputErrorPrompt,
+ BeeToolNoResultsPrompt,
+ BeeUserPrompt,
+} from "@/agents/bee/prompts.js";
export interface BeeRunInput {
prompt: string;
@@ -112,3 +119,11 @@ export interface BeeCallbacks {
meta: BeeMeta;
}>;
}
+
+export interface BeeAgentTemplates {
+ system: typeof BeeSystemPrompt;
+ user: typeof BeeUserPrompt;
+ toolError: typeof BeeToolErrorPrompt;
+ toolInputError: typeof BeeToolInputErrorPrompt;
+ toolNoResultError: typeof BeeToolNoResultsPrompt;
+}
From 777c684d7a154b48c441515605127e017983128d Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 14:40:14 +0200
Subject: [PATCH 28/35] fix(tool): handle default values in JSON/zod schema
---
src/internals/helpers/schema.ts | 10 +++++++++-
src/tools/base.ts | 6 +++++-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/internals/helpers/schema.ts b/src/internals/helpers/schema.ts
index e158c9a..7185937 100644
--- a/src/internals/helpers/schema.ts
+++ b/src/internals/helpers/schema.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { TypeOf, ZodType, ZodEffects, ZodTypeAny, AnyZodObject } from "zod";
+import { TypeOf, ZodType, ZodEffects, ZodTypeAny, AnyZodObject, input } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import { Ajv, SchemaObject, ValidateFunction, Options as AjvOptions } from "ajv";
import addFormats from "ajv-formats";
@@ -26,6 +26,7 @@ import { FrameworkErrorOptions, ValueError } from "@/errors.js";
export type AnyToolSchemaLike = AnyZodObject | SchemaObject;
export type AnySchemaLike = ZodTypeAny | SchemaObject;
export type FromSchemaLike = T extends ZodTypeAny ? TypeOf : unknown;
+export type FromSchemaLikeRaw = T extends ZodTypeAny ? input : unknown;
export function validateSchema(
schema: T | ZodEffects,
@@ -57,6 +58,13 @@ export function createSchemaValidator(
const ajv = new Ajv(
options ?? {
coerceTypes: true,
+ useDefaults: true,
+ strict: false,
+ strictSchema: false,
+ strictTuples: true,
+ strictNumbers: true,
+ strictTypes: true,
+ strictRequired: true,
},
);
addFormats.default(ajv);
diff --git a/src/tools/base.ts b/src/tools/base.ts
index 7cc722a..bf66816 100644
--- a/src/tools/base.ts
+++ b/src/tools/base.ts
@@ -27,6 +27,7 @@ import {
AnyToolSchemaLike,
createSchemaValidator,
FromSchemaLike,
+ FromSchemaLikeRaw,
toJsonSchema,
validateSchema,
} from "@/internals/helpers/schema.js";
@@ -149,6 +150,9 @@ export interface ToolSnapshot = FromSchemaLike>>;
+export type ToolInputRaw = FromSchemaLikeRaw<
+ Awaited>
+>;
type ToolConstructorParameters =
Partial extends TOptions ? [options?: TOptions] : [options: TOptions];
@@ -198,7 +202,7 @@ export abstract class Tool<
}
}
- run(input: ToolInput, options?: TRunOptions): Promise {
+ run(input: ToolInputRaw, options?: TRunOptions): Promise {
return RunContext.enter(this, async (run) => {
const meta = { input, options };
let errorPropagated = false;
From d4faf0052070b78e6dc0c408f8a37755536cfa1f Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 14:51:41 +0200
Subject: [PATCH 29/35] feat(tool): improve arxiv error handling
---
src/tools/arxiv.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/tools/arxiv.ts b/src/tools/arxiv.ts
index 8198d0b..dba0932 100644
--- a/src/tools/arxiv.ts
+++ b/src/tools/arxiv.ts
@@ -217,6 +217,7 @@ export class ArXivTool extends Tool
Date: Tue, 3 Sep 2024 14:52:11 +0200
Subject: [PATCH 30/35] test(tool): add arxiv e2e test
---
tests/e2e/tools/arxiv.test.ts | 72 +++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 tests/e2e/tools/arxiv.test.ts
diff --git a/tests/e2e/tools/arxiv.test.ts b/tests/e2e/tools/arxiv.test.ts
new file mode 100644
index 0000000..37d92f0
--- /dev/null
+++ b/tests/e2e/tools/arxiv.test.ts
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2024 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ArXivResponse, ArXivTool } from "@/tools/arxiv.js";
+import { beforeEach, expect } from "vitest";
+
+describe("Arxiv", () => {
+ let instance: ArXivTool;
+
+ beforeEach(() => {
+ instance = new ArXivTool();
+ });
+
+ it("Runs", async () => {
+ const response = await instance.run(
+ {
+ filters: [
+ {
+ include: [
+ {
+ value: "LLM",
+ field: "title",
+ },
+ ],
+ },
+ ],
+ maxResults: 2,
+ sort: {
+ type: "submittedDate",
+ order: "ascending",
+ },
+ },
+ {
+ signal: AbortSignal.timeout(60 * 1000),
+ retryOptions: {},
+ },
+ );
+
+ expect(response.isEmpty()).toBe(false);
+ expect(response.result.startIndex).toBe(0);
+ expect(response.result.entries.length).toBe(2);
+ for (const entry of response.result.entries) {
+ expect(entry).toMatchObject({
+ id: expect.any(String),
+ title: expect.any(String),
+ summary: expect.any(String),
+ published: expect.any(String),
+ } as ArXivResponse["entries"][0]);
+ }
+ });
+
+ it("Throws", async () => {
+ await expect(
+ instance.run({
+ id_list: ["xx"],
+ }),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`[ToolError: Request to ArXiv API has failed!]`);
+ });
+});
From 8a5fa4d9e086ce3b6949cff93176fc3988a419cb Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 14:54:40 +0200
Subject: [PATCH 31/35] chore: release 0.0.8
---
CHANGELOG.md | 12 ++++++++++++
package.json | 2 +-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c4e41b..0e7e96a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## [0.0.8](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.7...v0.0.8) (2024-09-03)
+
+### Features
+
+- **agent:** add support for overriding templates ([58c89d9](https://github.com/i-am-bee/bee-agent-framework/commit/58c89d93b737e5221ffb2f4c63f8e1cf508a4c33))
+- **tool:** improve arxiv error handling ([d4faf00](https://github.com/i-am-bee/bee-agent-framework/commit/d4faf0052070b78e6dc0c408f8a37755536cfa1f))
+- **tool:** stop using getters in custom tool ([0e25450](https://github.com/i-am-bee/bee-agent-framework/commit/0e254504cb197832fa5ed98188d0f62489def7c2))
+
+### Bug Fixes
+
+- **tool:** handle default values in JSON/zod schema ([777c684](https://github.com/i-am-bee/bee-agent-framework/commit/777c684d7a154b48c441515605127e017983128d))
+
## [0.0.7](https://github.com/i-am-bee/bee-agent-framework/compare/v0.0.6...v0.0.7) (2024-09-02)
### Features
diff --git a/package.json b/package.json
index c7f3b14..42e3528 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bee-agent-framework",
- "version": "0.0.7",
+ "version": "0.0.8",
"license": "Apache-2.0",
"description": "Bee - LLM Agent Framework",
"author": "IBM Corp.",
From e8d4f50c208a025ce7962d6f4314f878de07afe1 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 14:56:13 +0200
Subject: [PATCH 32/35] chore(emitter): remove console.log
---
src/emitter/utils.ts | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/emitter/utils.ts b/src/emitter/utils.ts
index b7765a4..a64c05e 100644
--- a/src/emitter/utils.ts
+++ b/src/emitter/utils.ts
@@ -25,8 +25,13 @@ export function assertValidNamespace(path: string[]) {
export function assertValidName(name: string) {
if (!name || !/(^[a-zA-Z_]+$)/.test(name)) {
- console.info({ name });
- throw new EmitterError("Name must contain only letters (optionally underscores).");
+ throw new EmitterError(
+ "Event name or a namespace part must contain only letters (optionally underscores).",
+ [],
+ {
+ context: { value: name },
+ },
+ );
}
}
From 01e63e4b66a3577cc0f150ca4c4f14ee428e8855 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 15:00:02 +0200
Subject: [PATCH 33/35] fix(agent): make templates property partial
---
src/agents/bee/agent.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/agents/bee/agent.ts b/src/agents/bee/agent.ts
index 6753574..9776343 100644
--- a/src/agents/bee/agent.ts
+++ b/src/agents/bee/agent.ts
@@ -42,7 +42,7 @@ export interface BeeInput {
tools: AnyTool[];
memory: BaseMemory;
meta?: AgentMeta;
- templates?: BeeAgentTemplates;
+ templates?: Partial;
}
export class BeeAgent extends BaseAgent {
From 5d85086eab34879ccdd9564afc054a7ace3f228a Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 15:36:30 +0200
Subject: [PATCH 34/35] docs: add SECURITY.md
---
SECURITY.md | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 SECURITY.md
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..abd8d81
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,15 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+To report vulnerabilities, you can privately report a potential security issue
+via the GitHub security vulnerabilities feature. This can be done here:
+
+https://github.com/i-am-bee/bee-agent-framework/security/advisories
+
+Please do **not** open a public issue about a potential security vulnerability.
+
+You can find more details on the security vulnerability feature in the GitHub
+documentation here:
+
+https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability
From 50f7d725697abd1b16184314897451b43d1e8d31 Mon Sep 17 00:00:00 2001
From: Tomas Dvorak
Date: Tue, 3 Sep 2024 19:00:30 +0200
Subject: [PATCH 35/35] feat(examples): use bee-agent-framework in imports
---
.husky/pre-commit | 2 +-
.lintstagedrc.json | 1 -
eslint.config.js | 20 +++++++++++++++++---
examples/agents/bee.ts | 20 ++++++++++----------
examples/agents/bee_reusable.ts | 8 ++++----
examples/agents/simple.ts | 10 +++++-----
examples/helpers/setup.ts | 2 +-
examples/llms/chat.ts | 6 +++---
examples/llms/chatCallback.ts | 6 +++---
examples/llms/chatStream.ts | 6 +++---
examples/llms/providers/bam.ts | 6 +++---
examples/llms/providers/langchain.ts | 4 ++--
examples/llms/providers/ollama.ts | 6 +++---
examples/llms/providers/openai.ts | 4 ++--
examples/llms/providers/watsonx.ts | 4 ++--
examples/llms/providers/watsonx_verbose.ts | 8 ++++----
examples/llms/structured.ts | 4 ++--
examples/llms/text.ts | 2 +-
examples/template.ts | 6 +++---
examples/tools/helloWorld.ts | 2 +-
examples/tools/openLibrary.ts | 6 +++---
examples/tsconfig.json | 13 +++++++++++++
package.json | 8 +++++---
tsconfig.examples.json | 3 +++
tsconfig.json | 3 ++-
vitest.config.ts | 6 +++++-
yarn.lock | 22 ++++++++++++++++------
27 files changed, 117 insertions(+), 71 deletions(-)
create mode 100644 examples/tsconfig.json
create mode 100644 tsconfig.examples.json
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 85e04b3..c90a5be 100644
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,2 +1,2 @@
yarn lint-staged
-CI=true yarn lint && yarn run test:unit && yarn copyright
+CI=true yarn lint && yarn ts:check && yarn run test:unit && yarn copyright
diff --git a/.lintstagedrc.json b/.lintstagedrc.json
index 325e2b5..7a273a6 100644
--- a/.lintstagedrc.json
+++ b/.lintstagedrc.json
@@ -1,5 +1,4 @@
{
"*.{ts,js}": "eslint --fix",
- "*.ts": "tsc-files --noEmit",
"*": "prettier --ignore-unknown --write"
}
diff --git a/eslint.config.js b/eslint.config.js
index 5e73ec5..e98a38a 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,6 +1,5 @@
// @ts-check
-// @ts-expect-error missing types
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier";
@@ -70,11 +69,26 @@ export default tseslint.config(
},
},
{
- files: ["examples/**"],
+ files: ["examples/**/*.ts"],
+ languageOptions: {
+ parserOptions: {
+ project: "./tsconfig.examples.json",
+ },
+ },
rules: {
- "no-restricted-imports": "off",
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-vars": "off",
+ "no-restricted-imports": [
+ "error",
+ {
+ patterns: [
+ {
+ group: ["@/"],
+ message: "Use 'bee-agent-framework' instead.",
+ },
+ ],
+ },
+ ],
},
},
// @ts-expect-error wrong types
diff --git a/examples/agents/bee.ts b/examples/agents/bee.ts
index 76094f9..50737d3 100644
--- a/examples/agents/bee.ts
+++ b/examples/agents/bee.ts
@@ -1,17 +1,17 @@
import "dotenv/config.js";
-import { BeeAgent } from "@/agents/bee/agent.js";
+import { BeeAgent } from "bee-agent-framework/agents/bee/agent";
import { createConsoleReader } from "../helpers/io.js";
-import { FrameworkError } from "@/errors.js";
-import { TokenMemory } from "@/memory/tokenMemory.js";
-import { Logger } from "@/logger/logger.js";
-import { PythonTool } from "@/tools/python/python.js";
-import { LocalPythonStorage } from "@/tools/python/storage.js";
-import { DuckDuckGoSearchTool } from "@/tools/search/duckDuckGoSearch.js";
-import { WikipediaTool } from "@/tools/search/wikipedia.js";
-import { OpenMeteoTool } from "@/tools/weather/openMeteo.js";
+import { FrameworkError } from "bee-agent-framework/errors";
+import { TokenMemory } from "bee-agent-framework/memory/tokenMemory";
+import { Logger } from "bee-agent-framework/logger/logger";
+import { PythonTool } from "bee-agent-framework/tools/python/python";
+import { LocalPythonStorage } from "bee-agent-framework/tools/python/storage";
+import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch";
+import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia";
+import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
Logger.root.level = "silent"; // disable internal logs
const logger = new Logger({ name: "app", level: "trace" });
diff --git a/examples/agents/bee_reusable.ts b/examples/agents/bee_reusable.ts
index 2f315d0..799e0a0 100644
--- a/examples/agents/bee_reusable.ts
+++ b/examples/agents/bee_reusable.ts
@@ -1,8 +1,8 @@
import "dotenv/config.js";
-import { BeeAgent } from "@/agents/bee/agent.js";
-import { DuckDuckGoSearchTool } from "@/tools/search/duckDuckGoSearch.js";
-import { UnconstrainedMemory } from "@/memory/unconstrainedMemory.js";
-import { OpenAIChatLLM } from "@/adapters/openai/chat.js";
+import { BeeAgent } from "bee-agent-framework/agents/bee/agent";
+import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch";
+import { UnconstrainedMemory } from "bee-agent-framework/memory/unconstrainedMemory";
+import { OpenAIChatLLM } from "bee-agent-framework/adapters/openai/chat";
// We create an agent
let agent = new BeeAgent({
diff --git a/examples/agents/simple.ts b/examples/agents/simple.ts
index 9566bcb..af4161f 100644
--- a/examples/agents/simple.ts
+++ b/examples/agents/simple.ts
@@ -1,9 +1,9 @@
import "dotenv/config.js";
-import { BeeAgent } from "@/agents/bee/agent.js";
-import { TokenMemory } from "@/memory/tokenMemory.js";
-import { DuckDuckGoSearchTool } from "@/tools/search/duckDuckGoSearch.js";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
-import { OpenMeteoTool } from "@/tools/weather/openMeteo.js";
+import { BeeAgent } from "bee-agent-framework/agents/bee/agent";
+import { TokenMemory } from "bee-agent-framework/memory/tokenMemory";
+import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
+import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo";
const llm = new OllamaChatLLM();
const agent = new BeeAgent({
diff --git a/examples/helpers/setup.ts b/examples/helpers/setup.ts
index 3286e2c..3ca01d3 100644
--- a/examples/helpers/setup.ts
+++ b/examples/helpers/setup.ts
@@ -1,4 +1,4 @@
import "dotenv/config";
-import { Logger } from "@/logger/logger.js";
+import { Logger } from "bee-agent-framework/logger/logger";
Logger.defaults.pretty = true;
diff --git a/examples/llms/chat.ts b/examples/llms/chat.ts
index 79d09ca..80c411a 100644
--- a/examples/llms/chat.ts
+++ b/examples/llms/chat.ts
@@ -1,8 +1,8 @@
import "dotenv/config.js";
import { createConsoleReader } from "examples/helpers/io.js";
-import { Logger } from "@/logger/logger.js";
-import { BaseMessage, Role } from "@/llms/primitives/message.js";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
+import { Logger } from "bee-agent-framework/logger/logger";
+import { BaseMessage, Role } from "bee-agent-framework/llms/primitives/message";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
Logger.root.level = "info"; // or your custom level
diff --git a/examples/llms/chatCallback.ts b/examples/llms/chatCallback.ts
index 8ccc9b9..d0c320a 100644
--- a/examples/llms/chatCallback.ts
+++ b/examples/llms/chatCallback.ts
@@ -1,8 +1,8 @@
import "dotenv/config.js";
import { createConsoleReader } from "examples/helpers/io.js";
-import { Logger } from "@/logger/logger.js";
-import { BaseMessage, Role } from "@/llms/primitives/message.js";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
+import { Logger } from "bee-agent-framework/logger/logger";
+import { BaseMessage, Role } from "bee-agent-framework/llms/primitives/message";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
Logger.root.level = "info"; // or your custom level
diff --git a/examples/llms/chatStream.ts b/examples/llms/chatStream.ts
index 8d77948..e1cafcd 100644
--- a/examples/llms/chatStream.ts
+++ b/examples/llms/chatStream.ts
@@ -1,8 +1,8 @@
import "dotenv/config.js";
import { createConsoleReader } from "examples/helpers/io.js";
-import { Logger } from "@/logger/logger.js";
-import { BaseMessage, Role } from "@/llms/primitives/message.js";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
+import { Logger } from "bee-agent-framework/logger/logger";
+import { BaseMessage, Role } from "bee-agent-framework/llms/primitives/message";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
Logger.root.level = "info"; // or your custom level
diff --git a/examples/llms/providers/bam.ts b/examples/llms/providers/bam.ts
index 5e87dad..ce550c6 100644
--- a/examples/llms/providers/bam.ts
+++ b/examples/llms/providers/bam.ts
@@ -1,6 +1,6 @@
-import { BaseMessage } from "@/llms/primitives/message.js";
-import { BAMLLM } from "@/adapters/bam/llm.js";
-import { BAMChatLLM } from "@/adapters/bam/chat.js";
+import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
+import { BAMLLM } from "bee-agent-framework/adapters/bam/llm";
+import { BAMChatLLM } from "bee-agent-framework/adapters/bam/chat";
{
console.info("===RAW===");
diff --git a/examples/llms/providers/langchain.ts b/examples/llms/providers/langchain.ts
index 3bf4806..18c53f4 100644
--- a/examples/llms/providers/langchain.ts
+++ b/examples/llms/providers/langchain.ts
@@ -3,8 +3,8 @@
// - @langchain/cohere (or any other provider related package that you would like to use)
// List of available providers: https://js.langchain.com/v0.2/docs/integrations/chat/
-import { BaseMessage } from "@/llms/primitives/message.js";
-import { LangChainChatLLM } from "@/adapters/langchain/llms/chat.js";
+import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
+import { LangChainChatLLM } from "bee-agent-framework/adapters/langchain/llms/chat";
// @ts-expect-error package not installed
import { ChatCohere } from "@langchain/cohere";
diff --git a/examples/llms/providers/ollama.ts b/examples/llms/providers/ollama.ts
index 200b175..1d2fef7 100644
--- a/examples/llms/providers/ollama.ts
+++ b/examples/llms/providers/ollama.ts
@@ -1,6 +1,6 @@
-import { OllamaLLM } from "@/adapters/ollama/llm.js";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
-import { BaseMessage } from "@/llms/primitives/message.js";
+import { OllamaLLM } from "bee-agent-framework/adapters/ollama/llm";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
+import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
{
console.info("===RAW===");
diff --git a/examples/llms/providers/openai.ts b/examples/llms/providers/openai.ts
index 5b28ecb..b56d045 100644
--- a/examples/llms/providers/openai.ts
+++ b/examples/llms/providers/openai.ts
@@ -1,6 +1,6 @@
import "dotenv/config";
-import { BaseMessage } from "@/llms/primitives/message.js";
-import { OpenAIChatLLM } from "@/adapters/openai/chat.js";
+import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
+import { OpenAIChatLLM } from "bee-agent-framework/adapters/openai/chat";
const llm = new OpenAIChatLLM({
modelId: "gpt-4o",
diff --git a/examples/llms/providers/watsonx.ts b/examples/llms/providers/watsonx.ts
index da37e57..44cba8b 100644
--- a/examples/llms/providers/watsonx.ts
+++ b/examples/llms/providers/watsonx.ts
@@ -1,6 +1,6 @@
import "dotenv/config";
-import { BaseMessage } from "@/llms/primitives/message.js";
-import { WatsonXChatLLM } from "@/adapters/watsonx/chat.js";
+import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
+import { WatsonXChatLLM } from "bee-agent-framework/adapters/watsonx/chat";
const chatLLM = WatsonXChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct", {
apiKey: process.env.WATSONX_API_KEY,
diff --git a/examples/llms/providers/watsonx_verbose.ts b/examples/llms/providers/watsonx_verbose.ts
index 6ebff6c..999ac80 100644
--- a/examples/llms/providers/watsonx_verbose.ts
+++ b/examples/llms/providers/watsonx_verbose.ts
@@ -1,8 +1,8 @@
import "dotenv/config";
-import { BaseMessage } from "@/llms/primitives/message.js";
-import { WatsonXChatLLM } from "@/adapters/watsonx/chat.js";
-import { WatsonXLLM } from "@/adapters/watsonx/llm.js";
-import { PromptTemplate } from "@/template.js";
+import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
+import { WatsonXChatLLM } from "bee-agent-framework/adapters/watsonx/chat";
+import { WatsonXLLM } from "bee-agent-framework/adapters/watsonx/llm";
+import { PromptTemplate } from "bee-agent-framework/template";
const template = new PromptTemplate({
variables: ["messages"],
diff --git a/examples/llms/structured.ts b/examples/llms/structured.ts
index 765010e..4708e16 100644
--- a/examples/llms/structured.ts
+++ b/examples/llms/structured.ts
@@ -1,7 +1,7 @@
import "dotenv/config.js";
import { z } from "zod";
-import { BaseMessage, Role } from "@/llms/primitives/message.js";
-import { OllamaChatLLM } from "@/adapters/ollama/chat.js";
+import { BaseMessage, Role } from "bee-agent-framework/llms/primitives/message";
+import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
const llm = new OllamaChatLLM();
const response = await llm.generateStructured(
diff --git a/examples/llms/text.ts b/examples/llms/text.ts
index ccc33a1..2d7fffe 100644
--- a/examples/llms/text.ts
+++ b/examples/llms/text.ts
@@ -1,6 +1,6 @@
import "dotenv/config.js";
import { createConsoleReader } from "examples/helpers/io.js";
-import { WatsonXLLM } from "@/adapters/watsonx/llm.js";
+import { WatsonXLLM } from "bee-agent-framework/adapters/watsonx/llm";
const llm = new WatsonXLLM({
modelId: "google/flan-ul2",
diff --git a/examples/template.ts b/examples/template.ts
index cc77bc7..d870dc1 100644
--- a/examples/template.ts
+++ b/examples/template.ts
@@ -1,6 +1,6 @@
-import "examples/helpers/setup.js";
-import { PromptTemplate } from "@/template.js";
-import { Logger } from "@/logger/logger.js";
+import "./helpers/setup.js";
+import { PromptTemplate } from "bee-agent-framework/template";
+import { Logger } from "bee-agent-framework/logger/logger";
const logger = new Logger({ name: "template" });
diff --git a/examples/tools/helloWorld.ts b/examples/tools/helloWorld.ts
index a8a3b20..3669fe4 100644
--- a/examples/tools/helloWorld.ts
+++ b/examples/tools/helloWorld.ts
@@ -4,7 +4,7 @@ import {
StringToolOutput,
Tool,
ToolInput,
-} from "@/tools/base.js";
+} from "bee-agent-framework/tools/base";
import { z } from "zod";
type ToolOptions = BaseToolOptions;
diff --git a/examples/tools/openLibrary.ts b/examples/tools/openLibrary.ts
index 4b28fc3..79d6d0f 100644
--- a/examples/tools/openLibrary.ts
+++ b/examples/tools/openLibrary.ts
@@ -5,9 +5,9 @@ import {
ToolInput,
JSONToolOutput,
ToolError,
-} from "@/tools/base.js";
+} from "bee-agent-framework/tools/base";
import { z } from "zod";
-import { createURLParams } from "@/internals/fetcher.js";
+import { createURLParams } from "bee-agent-framework/internals/fetcher";
type ToolOptions = BaseToolOptions;
type ToolRunOptions = BaseToolRunOptions;
@@ -117,7 +117,7 @@ export class OpenLibraryTool extends Tool, options?: ToolRunOptions) {
const params = createURLParams(input);
- const url = `https://openlibrary.org/search.json?${decodeURIComponent(params.toString())}`;
+ const url = `https://openlibrary.org/searchon?${decodeURIComponent(params.toString())}`;
const response = await fetch(url, {
signal: options?.signal,
});
diff --git a/examples/tsconfig.json b/examples/tsconfig.json
new file mode 100644
index 0000000..e0fca6a
--- /dev/null
+++ b/examples/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": "..",
+ "rootDir": "..",
+ "paths": {
+ "bee-agent-framework/*": ["./src/*.js"],
+ "@/*": ["./src/*"]
+ }
+ },
+ "references": [{ "path": "./src" }],
+ "exclude": ["../tests", "../**/*.test.ts"]
+}
diff --git a/package.json b/package.json
index 42e3528..a8e89c4 100644
--- a/package.json
+++ b/package.json
@@ -54,8 +54,9 @@
"scripts": {
"clean": "rimraf dist",
"build": "yarn clean && yarn ts:check && tsup",
- "ts:check": "tsc --noEmit",
- "start:bee": "tsx examples/agents/bee.ts",
+ "ts:check": "tsc --noEmit && tsc -p tsconfig.examples.json --noEmit",
+ "start": "tsx --tsconfig tsconfig.examples.json",
+ "start:bee": "yarn start -- examples/agents/bee.ts",
"infra:start-all": "yarn _docker compose up -d",
"infra:start-code-interpreter": "yarn _docker compose up bee-code-interpreter",
"infra:stop-all": "yarn _docker compose down",
@@ -126,6 +127,7 @@
"@swc/core": "^1.7.14",
"@types/eslint": "^9.6.1",
"@types/eslint-config-prettier": "^6.11.3",
+ "@types/eslint__js": "^8.42.3",
"@types/mustache": "^4",
"@types/needle": "^3.3.0",
"@types/node": "^20.16.1",
@@ -154,7 +156,7 @@
"temp-dir": "^3.0.0",
"tsc-files": "^1.1.4",
"tsup": "^8.2.4",
- "tsx": "^4.17.0",
+ "tsx": "^4.19.0",
"typescript": "^5.5.4",
"typescript-eslint": "^8.2.0",
"vite-tsconfig-paths": "^5.0.1",
diff --git a/tsconfig.examples.json b/tsconfig.examples.json
new file mode 100644
index 0000000..4a70142
--- /dev/null
+++ b/tsconfig.examples.json
@@ -0,0 +1,3 @@
+{
+ "extends": "./examples/tsconfig.json"
+}
diff --git a/tsconfig.json b/tsconfig.json
index ac40f78..e874ce6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -29,5 +29,6 @@
"@tests/*": ["./tests/*"]
},
"useUnknownInCatchVariables": false
- }
+ },
+ "exclude": ["examples"]
}
diff --git a/vitest.config.ts b/vitest.config.ts
index eaf7f3d..3b98d0c 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -16,5 +16,9 @@ export default defineConfig({
define: {
__LIBRARY_VERSION: JSON.stringify(packageJson.version),
},
- plugins: [tsConfigPaths()],
+ plugins: [
+ tsConfigPaths({
+ projects: ["tsconfig.json"],
+ }),
+ ],
});
diff --git a/yarn.lock b/yarn.lock
index 07c7dea..d46cfe8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1836,7 +1836,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/eslint@npm:^9.6.1":
+"@types/eslint@npm:*, @types/eslint@npm:^9.6.1":
version: 9.6.1
resolution: "@types/eslint@npm:9.6.1"
dependencies:
@@ -1846,6 +1846,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/eslint__js@npm:^8.42.3":
+ version: 8.42.3
+ resolution: "@types/eslint__js@npm:8.42.3"
+ dependencies:
+ "@types/eslint": "npm:*"
+ checksum: 10c0/ccc5180b92155929a089ffb03ed62625216dcd5e46dd3197c6f82370ce8b52c7cb9df66c06b0a3017995409e023bc9eafe5a3f009e391960eacefaa1b62d9a56
+ languageName: node
+ linkType: hard
+
"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0":
version: 1.0.5
resolution: "@types/estree@npm:1.0.5"
@@ -2508,6 +2517,7 @@ __metadata:
"@swc/core": "npm:^1.7.14"
"@types/eslint": "npm:^9.6.1"
"@types/eslint-config-prettier": "npm:^6.11.3"
+ "@types/eslint__js": "npm:^8.42.3"
"@types/mustache": "npm:^4"
"@types/needle": "npm:^3.3.0"
"@types/node": "npm:^20.16.1"
@@ -2554,7 +2564,7 @@ __metadata:
temp-dir: "npm:^3.0.0"
tsc-files: "npm:^1.1.4"
tsup: "npm:^8.2.4"
- tsx: "npm:^4.17.0"
+ tsx: "npm:^4.19.0"
turndown: "npm:^7.2.0"
typescript: "npm:^5.5.4"
typescript-eslint: "npm:^8.2.0"
@@ -8949,9 +8959,9 @@ __metadata:
languageName: node
linkType: hard
-"tsx@npm:^4.17.0":
- version: 4.17.0
- resolution: "tsx@npm:4.17.0"
+"tsx@npm:^4.19.0":
+ version: 4.19.0
+ resolution: "tsx@npm:4.19.0"
dependencies:
esbuild: "npm:~0.23.0"
fsevents: "npm:~2.3.3"
@@ -8961,7 +8971,7 @@ __metadata:
optional: true
bin:
tsx: dist/cli.mjs
- checksum: 10c0/ad720b81d6447c7695d24c27947fa1a2b6db9d2ef03216389edd6fa0006aa479bc0d8348a1ac9975a08edef4ce791ff5629a24d8dccbb0987f42e5407785cfa4
+ checksum: 10c0/d14463a78067c6db84c677b79b14861de6d7f6fb0ffa5727cc500c4552459e936395a3854ad0112af0fd7b263bcedd62ce3929b036188eb10cd9902a607ffe34
languageName: node
linkType: hard